| .. | .. |
|---|
| 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 | } |
|---|