.. | .. |
---|
71 | 71 | #define MBOCHS_NAME "mbochs" |
---|
72 | 72 | #define MBOCHS_CLASS_NAME "mbochs" |
---|
73 | 73 | |
---|
| 74 | +#define MBOCHS_EDID_REGION_INDEX VFIO_PCI_NUM_REGIONS |
---|
| 75 | +#define MBOCHS_NUM_REGIONS (MBOCHS_EDID_REGION_INDEX+1) |
---|
| 76 | + |
---|
74 | 77 | #define MBOCHS_CONFIG_SPACE_SIZE 0xff |
---|
75 | 78 | #define MBOCHS_MMIO_BAR_OFFSET PAGE_SIZE |
---|
76 | 79 | #define MBOCHS_MMIO_BAR_SIZE PAGE_SIZE |
---|
77 | | -#define MBOCHS_MEMORY_BAR_OFFSET (MBOCHS_MMIO_BAR_OFFSET + \ |
---|
| 80 | +#define MBOCHS_EDID_OFFSET (MBOCHS_MMIO_BAR_OFFSET + \ |
---|
78 | 81 | MBOCHS_MMIO_BAR_SIZE) |
---|
| 82 | +#define MBOCHS_EDID_SIZE PAGE_SIZE |
---|
| 83 | +#define MBOCHS_MEMORY_BAR_OFFSET (MBOCHS_EDID_OFFSET + \ |
---|
| 84 | + MBOCHS_EDID_SIZE) |
---|
| 85 | + |
---|
| 86 | +#define MBOCHS_EDID_BLOB_OFFSET (MBOCHS_EDID_SIZE/2) |
---|
79 | 87 | |
---|
80 | 88 | #define STORE_LE16(addr, val) (*(u16 *)addr = val) |
---|
81 | 89 | #define STORE_LE32(addr, val) (*(u32 *)addr = val) |
---|
.. | .. |
---|
95 | 103 | static const struct mbochs_type { |
---|
96 | 104 | const char *name; |
---|
97 | 105 | u32 mbytes; |
---|
| 106 | + u32 max_x; |
---|
| 107 | + u32 max_y; |
---|
98 | 108 | } mbochs_types[] = { |
---|
99 | 109 | { |
---|
100 | 110 | .name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_1, |
---|
101 | 111 | .mbytes = 4, |
---|
| 112 | + .max_x = 800, |
---|
| 113 | + .max_y = 600, |
---|
102 | 114 | }, { |
---|
103 | 115 | .name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_2, |
---|
104 | 116 | .mbytes = 16, |
---|
| 117 | + .max_x = 1920, |
---|
| 118 | + .max_y = 1440, |
---|
105 | 119 | }, { |
---|
106 | 120 | .name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_3, |
---|
107 | 121 | .mbytes = 64, |
---|
| 122 | + .max_x = 0, |
---|
| 123 | + .max_y = 0, |
---|
108 | 124 | }, |
---|
109 | 125 | }; |
---|
110 | 126 | |
---|
.. | .. |
---|
114 | 130 | static struct cdev mbochs_cdev; |
---|
115 | 131 | static struct device mbochs_dev; |
---|
116 | 132 | static int mbochs_used_mbytes; |
---|
| 133 | + |
---|
| 134 | +struct vfio_region_info_ext { |
---|
| 135 | + struct vfio_region_info base; |
---|
| 136 | + struct vfio_region_info_cap_type type; |
---|
| 137 | +}; |
---|
117 | 138 | |
---|
118 | 139 | struct mbochs_mode { |
---|
119 | 140 | u32 drm_format; |
---|
.. | .. |
---|
144 | 165 | u32 memory_bar_mask; |
---|
145 | 166 | struct mutex ops_lock; |
---|
146 | 167 | struct mdev_device *mdev; |
---|
147 | | - struct vfio_device_info dev_info; |
---|
148 | 168 | |
---|
149 | 169 | const struct mbochs_type *type; |
---|
150 | 170 | u16 vbe[VBE_DISPI_INDEX_COUNT]; |
---|
151 | 171 | u64 memsize; |
---|
152 | 172 | struct page **pages; |
---|
153 | 173 | pgoff_t pagecount; |
---|
| 174 | + struct vfio_region_gfx_edid edid_regs; |
---|
| 175 | + u8 edid_blob[0x400]; |
---|
154 | 176 | |
---|
155 | 177 | struct list_head dmabufs; |
---|
156 | 178 | u32 active_id; |
---|
.. | .. |
---|
342 | 364 | char *buf, u32 count) |
---|
343 | 365 | { |
---|
344 | 366 | struct device *dev = mdev_dev(mdev_state->mdev); |
---|
| 367 | + struct vfio_region_gfx_edid *edid; |
---|
345 | 368 | u16 reg16 = 0; |
---|
346 | 369 | int index; |
---|
347 | 370 | |
---|
348 | 371 | switch (offset) { |
---|
| 372 | + case 0x000 ... 0x3ff: /* edid block */ |
---|
| 373 | + edid = &mdev_state->edid_regs; |
---|
| 374 | + if (edid->link_state != VFIO_DEVICE_GFX_LINK_STATE_UP || |
---|
| 375 | + offset >= edid->edid_size) { |
---|
| 376 | + memset(buf, 0, count); |
---|
| 377 | + break; |
---|
| 378 | + } |
---|
| 379 | + memcpy(buf, mdev_state->edid_blob + offset, count); |
---|
| 380 | + break; |
---|
349 | 381 | case 0x500 ... 0x515: /* bochs dispi interface */ |
---|
350 | 382 | if (count != 2) |
---|
351 | 383 | goto unhandled; |
---|
.. | .. |
---|
363 | 395 | memset(buf, 0, count); |
---|
364 | 396 | break; |
---|
365 | 397 | } |
---|
| 398 | +} |
---|
| 399 | + |
---|
| 400 | +static void handle_edid_regs(struct mdev_state *mdev_state, u16 offset, |
---|
| 401 | + char *buf, u32 count, bool is_write) |
---|
| 402 | +{ |
---|
| 403 | + char *regs = (void *)&mdev_state->edid_regs; |
---|
| 404 | + |
---|
| 405 | + if (offset + count > sizeof(mdev_state->edid_regs)) |
---|
| 406 | + return; |
---|
| 407 | + if (count != 4) |
---|
| 408 | + return; |
---|
| 409 | + if (offset % 4) |
---|
| 410 | + return; |
---|
| 411 | + |
---|
| 412 | + if (is_write) { |
---|
| 413 | + switch (offset) { |
---|
| 414 | + case offsetof(struct vfio_region_gfx_edid, link_state): |
---|
| 415 | + case offsetof(struct vfio_region_gfx_edid, edid_size): |
---|
| 416 | + memcpy(regs + offset, buf, count); |
---|
| 417 | + break; |
---|
| 418 | + default: |
---|
| 419 | + /* read-only regs */ |
---|
| 420 | + break; |
---|
| 421 | + } |
---|
| 422 | + } else { |
---|
| 423 | + memcpy(buf, regs + offset, count); |
---|
| 424 | + } |
---|
| 425 | +} |
---|
| 426 | + |
---|
| 427 | +static void handle_edid_blob(struct mdev_state *mdev_state, u16 offset, |
---|
| 428 | + char *buf, u32 count, bool is_write) |
---|
| 429 | +{ |
---|
| 430 | + if (offset + count > mdev_state->edid_regs.edid_max_size) |
---|
| 431 | + return; |
---|
| 432 | + if (is_write) |
---|
| 433 | + memcpy(mdev_state->edid_blob + offset, buf, count); |
---|
| 434 | + else |
---|
| 435 | + memcpy(buf, mdev_state->edid_blob + offset, count); |
---|
366 | 436 | } |
---|
367 | 437 | |
---|
368 | 438 | static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count, |
---|
.. | .. |
---|
384 | 454 | memcpy(buf, (mdev_state->vconfig + pos), count); |
---|
385 | 455 | |
---|
386 | 456 | } else if (pos >= MBOCHS_MMIO_BAR_OFFSET && |
---|
387 | | - pos + count <= MBOCHS_MEMORY_BAR_OFFSET) { |
---|
| 457 | + pos + count <= (MBOCHS_MMIO_BAR_OFFSET + |
---|
| 458 | + MBOCHS_MMIO_BAR_SIZE)) { |
---|
388 | 459 | pos -= MBOCHS_MMIO_BAR_OFFSET; |
---|
389 | 460 | if (is_write) |
---|
390 | 461 | handle_mmio_write(mdev_state, pos, buf, count); |
---|
391 | 462 | else |
---|
392 | 463 | handle_mmio_read(mdev_state, pos, buf, count); |
---|
| 464 | + |
---|
| 465 | + } else if (pos >= MBOCHS_EDID_OFFSET && |
---|
| 466 | + pos + count <= (MBOCHS_EDID_OFFSET + |
---|
| 467 | + MBOCHS_EDID_SIZE)) { |
---|
| 468 | + pos -= MBOCHS_EDID_OFFSET; |
---|
| 469 | + if (pos < MBOCHS_EDID_BLOB_OFFSET) { |
---|
| 470 | + handle_edid_regs(mdev_state, pos, buf, count, is_write); |
---|
| 471 | + } else { |
---|
| 472 | + pos -= MBOCHS_EDID_BLOB_OFFSET; |
---|
| 473 | + handle_edid_blob(mdev_state, pos, buf, count, is_write); |
---|
| 474 | + } |
---|
393 | 475 | |
---|
394 | 476 | } else if (pos >= MBOCHS_MEMORY_BAR_OFFSET && |
---|
395 | 477 | pos + count <= |
---|
.. | .. |
---|
471 | 553 | mdev_state->next_id = 1; |
---|
472 | 554 | |
---|
473 | 555 | mdev_state->type = type; |
---|
| 556 | + mdev_state->edid_regs.max_xres = type->max_x; |
---|
| 557 | + mdev_state->edid_regs.max_yres = type->max_y; |
---|
| 558 | + mdev_state->edid_regs.edid_offset = MBOCHS_EDID_BLOB_OFFSET; |
---|
| 559 | + mdev_state->edid_regs.edid_max_size = sizeof(mdev_state->edid_blob); |
---|
474 | 560 | mbochs_create_config_space(mdev_state); |
---|
475 | 561 | mbochs_reset(mdev); |
---|
476 | 562 | |
---|
.. | .. |
---|
760 | 846 | if (sg_alloc_table_from_pages(sg, dmabuf->pages, dmabuf->pagecount, |
---|
761 | 847 | 0, dmabuf->mode.size, GFP_KERNEL) < 0) |
---|
762 | 848 | goto err2; |
---|
763 | | - if (!dma_map_sg(at->dev, sg->sgl, sg->nents, direction)) |
---|
| 849 | + if (dma_map_sgtable(at->dev, sg, direction, 0)) |
---|
764 | 850 | goto err3; |
---|
765 | 851 | |
---|
766 | 852 | return sg; |
---|
.. | .. |
---|
782 | 868 | |
---|
783 | 869 | dev_dbg(dev, "%s: %d\n", __func__, dmabuf->id); |
---|
784 | 870 | |
---|
| 871 | + dma_unmap_sgtable(at->dev, sg, direction, 0); |
---|
785 | 872 | sg_free_table(sg); |
---|
786 | 873 | kfree(sg); |
---|
787 | 874 | } |
---|
.. | .. |
---|
805 | 892 | mutex_unlock(&mdev_state->ops_lock); |
---|
806 | 893 | } |
---|
807 | 894 | |
---|
808 | | -static void *mbochs_kmap_dmabuf(struct dma_buf *buf, unsigned long page_num) |
---|
809 | | -{ |
---|
810 | | - struct mbochs_dmabuf *dmabuf = buf->priv; |
---|
811 | | - struct page *page = dmabuf->pages[page_num]; |
---|
812 | | - |
---|
813 | | - return kmap(page); |
---|
814 | | -} |
---|
815 | | - |
---|
816 | | -static void mbochs_kunmap_dmabuf(struct dma_buf *buf, unsigned long page_num, |
---|
817 | | - void *vaddr) |
---|
818 | | -{ |
---|
819 | | - kunmap(vaddr); |
---|
820 | | -} |
---|
821 | | - |
---|
822 | 895 | static struct dma_buf_ops mbochs_dmabuf_ops = { |
---|
823 | 896 | .map_dma_buf = mbochs_map_dmabuf, |
---|
824 | 897 | .unmap_dma_buf = mbochs_unmap_dmabuf, |
---|
825 | 898 | .release = mbochs_release_dmabuf, |
---|
826 | | - .map = mbochs_kmap_dmabuf, |
---|
827 | | - .unmap = mbochs_kunmap_dmabuf, |
---|
828 | 899 | .mmap = mbochs_mmap_dmabuf, |
---|
829 | 900 | }; |
---|
830 | 901 | |
---|
.. | .. |
---|
932 | 1003 | } |
---|
933 | 1004 | |
---|
934 | 1005 | static int mbochs_get_region_info(struct mdev_device *mdev, |
---|
935 | | - struct vfio_region_info *region_info, |
---|
936 | | - u16 *cap_type_id, void **cap_type) |
---|
| 1006 | + struct vfio_region_info_ext *ext) |
---|
937 | 1007 | { |
---|
| 1008 | + struct vfio_region_info *region_info = &ext->base; |
---|
938 | 1009 | struct mdev_state *mdev_state; |
---|
939 | 1010 | |
---|
940 | 1011 | mdev_state = mdev_get_drvdata(mdev); |
---|
941 | 1012 | if (!mdev_state) |
---|
942 | 1013 | return -EINVAL; |
---|
943 | 1014 | |
---|
944 | | - if (region_info->index >= VFIO_PCI_NUM_REGIONS) |
---|
| 1015 | + if (region_info->index >= MBOCHS_NUM_REGIONS) |
---|
945 | 1016 | return -EINVAL; |
---|
946 | 1017 | |
---|
947 | 1018 | switch (region_info->index) { |
---|
.. | .. |
---|
964 | 1035 | region_info->flags = (VFIO_REGION_INFO_FLAG_READ | |
---|
965 | 1036 | VFIO_REGION_INFO_FLAG_WRITE); |
---|
966 | 1037 | break; |
---|
| 1038 | + case MBOCHS_EDID_REGION_INDEX: |
---|
| 1039 | + ext->base.argsz = sizeof(*ext); |
---|
| 1040 | + ext->base.offset = MBOCHS_EDID_OFFSET; |
---|
| 1041 | + ext->base.size = MBOCHS_EDID_SIZE; |
---|
| 1042 | + ext->base.flags = (VFIO_REGION_INFO_FLAG_READ | |
---|
| 1043 | + VFIO_REGION_INFO_FLAG_WRITE | |
---|
| 1044 | + VFIO_REGION_INFO_FLAG_CAPS); |
---|
| 1045 | + ext->base.cap_offset = offsetof(typeof(*ext), type); |
---|
| 1046 | + ext->type.header.id = VFIO_REGION_INFO_CAP_TYPE; |
---|
| 1047 | + ext->type.header.version = 1; |
---|
| 1048 | + ext->type.header.next = 0; |
---|
| 1049 | + ext->type.type = VFIO_REGION_TYPE_GFX; |
---|
| 1050 | + ext->type.subtype = VFIO_REGION_SUBTYPE_GFX_EDID; |
---|
| 1051 | + break; |
---|
967 | 1052 | default: |
---|
968 | 1053 | region_info->size = 0; |
---|
969 | 1054 | region_info->offset = 0; |
---|
.. | .. |
---|
984 | 1069 | struct vfio_device_info *dev_info) |
---|
985 | 1070 | { |
---|
986 | 1071 | dev_info->flags = VFIO_DEVICE_FLAGS_PCI; |
---|
987 | | - dev_info->num_regions = VFIO_PCI_NUM_REGIONS; |
---|
| 1072 | + dev_info->num_regions = MBOCHS_NUM_REGIONS; |
---|
988 | 1073 | dev_info->num_irqs = VFIO_PCI_NUM_IRQS; |
---|
989 | 1074 | return 0; |
---|
990 | 1075 | } |
---|
.. | .. |
---|
1084 | 1169 | unsigned long arg) |
---|
1085 | 1170 | { |
---|
1086 | 1171 | int ret = 0; |
---|
1087 | | - unsigned long minsz; |
---|
1088 | | - struct mdev_state *mdev_state; |
---|
1089 | | - |
---|
1090 | | - mdev_state = mdev_get_drvdata(mdev); |
---|
| 1172 | + unsigned long minsz, outsz; |
---|
1091 | 1173 | |
---|
1092 | 1174 | switch (cmd) { |
---|
1093 | 1175 | case VFIO_DEVICE_GET_INFO: |
---|
.. | .. |
---|
1106 | 1188 | if (ret) |
---|
1107 | 1189 | return ret; |
---|
1108 | 1190 | |
---|
1109 | | - memcpy(&mdev_state->dev_info, &info, sizeof(info)); |
---|
1110 | | - |
---|
1111 | 1191 | if (copy_to_user((void __user *)arg, &info, minsz)) |
---|
1112 | 1192 | return -EFAULT; |
---|
1113 | 1193 | |
---|
.. | .. |
---|
1115 | 1195 | } |
---|
1116 | 1196 | case VFIO_DEVICE_GET_REGION_INFO: |
---|
1117 | 1197 | { |
---|
1118 | | - struct vfio_region_info info; |
---|
1119 | | - u16 cap_type_id = 0; |
---|
1120 | | - void *cap_type = NULL; |
---|
| 1198 | + struct vfio_region_info_ext info; |
---|
1121 | 1199 | |
---|
1122 | | - minsz = offsetofend(struct vfio_region_info, offset); |
---|
| 1200 | + minsz = offsetofend(typeof(info), base.offset); |
---|
1123 | 1201 | |
---|
1124 | 1202 | if (copy_from_user(&info, (void __user *)arg, minsz)) |
---|
1125 | 1203 | return -EFAULT; |
---|
1126 | 1204 | |
---|
1127 | | - if (info.argsz < minsz) |
---|
| 1205 | + outsz = info.base.argsz; |
---|
| 1206 | + if (outsz < minsz) |
---|
| 1207 | + return -EINVAL; |
---|
| 1208 | + if (outsz > sizeof(info)) |
---|
1128 | 1209 | return -EINVAL; |
---|
1129 | 1210 | |
---|
1130 | | - ret = mbochs_get_region_info(mdev, &info, &cap_type_id, |
---|
1131 | | - &cap_type); |
---|
| 1211 | + ret = mbochs_get_region_info(mdev, &info); |
---|
1132 | 1212 | if (ret) |
---|
1133 | 1213 | return ret; |
---|
1134 | 1214 | |
---|
1135 | | - if (copy_to_user((void __user *)arg, &info, minsz)) |
---|
| 1215 | + if (copy_to_user((void __user *)arg, &info, outsz)) |
---|
1136 | 1216 | return -EFAULT; |
---|
1137 | 1217 | |
---|
1138 | 1218 | return 0; |
---|
.. | .. |
---|
1148 | 1228 | return -EFAULT; |
---|
1149 | 1229 | |
---|
1150 | 1230 | if ((info.argsz < minsz) || |
---|
1151 | | - (info.index >= mdev_state->dev_info.num_irqs)) |
---|
| 1231 | + (info.index >= VFIO_PCI_NUM_IRQS)) |
---|
1152 | 1232 | return -EINVAL; |
---|
1153 | 1233 | |
---|
1154 | 1234 | ret = mbochs_get_irq_info(mdev, &info); |
---|
.. | .. |
---|
1350 | 1430 | { |
---|
1351 | 1431 | int ret = 0; |
---|
1352 | 1432 | |
---|
1353 | | - ret = alloc_chrdev_region(&mbochs_devt, 0, MINORMASK, MBOCHS_NAME); |
---|
| 1433 | + ret = alloc_chrdev_region(&mbochs_devt, 0, MINORMASK + 1, MBOCHS_NAME); |
---|
1354 | 1434 | if (ret < 0) { |
---|
1355 | 1435 | pr_err("Error: failed to register mbochs_dev, err: %d\n", ret); |
---|
1356 | 1436 | return ret; |
---|
1357 | 1437 | } |
---|
1358 | 1438 | cdev_init(&mbochs_cdev, &vd_fops); |
---|
1359 | | - cdev_add(&mbochs_cdev, mbochs_devt, MINORMASK); |
---|
| 1439 | + cdev_add(&mbochs_cdev, mbochs_devt, MINORMASK + 1); |
---|
1360 | 1440 | pr_info("%s: major %d\n", __func__, MAJOR(mbochs_devt)); |
---|
1361 | 1441 | |
---|
1362 | 1442 | mbochs_class = class_create(THIS_MODULE, MBOCHS_CLASS_NAME); |
---|
.. | .. |
---|
1385 | 1465 | class_destroy(mbochs_class); |
---|
1386 | 1466 | failed1: |
---|
1387 | 1467 | cdev_del(&mbochs_cdev); |
---|
1388 | | - unregister_chrdev_region(mbochs_devt, MINORMASK); |
---|
| 1468 | + unregister_chrdev_region(mbochs_devt, MINORMASK + 1); |
---|
1389 | 1469 | return ret; |
---|
1390 | 1470 | } |
---|
1391 | 1471 | |
---|
.. | .. |
---|
1396 | 1476 | |
---|
1397 | 1477 | device_unregister(&mbochs_dev); |
---|
1398 | 1478 | cdev_del(&mbochs_cdev); |
---|
1399 | | - unregister_chrdev_region(mbochs_devt, MINORMASK); |
---|
| 1479 | + unregister_chrdev_region(mbochs_devt, MINORMASK + 1); |
---|
1400 | 1480 | class_destroy(mbochs_class); |
---|
1401 | 1481 | mbochs_class = NULL; |
---|
1402 | 1482 | } |
---|