Skip to content

Commit 104c740

Browse files
kraxelawilliam
authored andcommitted
vfio: add edid support to mbochs sample driver
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
1 parent 3cdf752 commit 104c740

File tree

1 file changed

+117
-19
lines changed

1 file changed

+117
-19
lines changed

samples/vfio-mdev/mbochs.c

Lines changed: 117 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,19 @@
7171
#define MBOCHS_NAME "mbochs"
7272
#define MBOCHS_CLASS_NAME "mbochs"
7373

74+
#define MBOCHS_EDID_REGION_INDEX VFIO_PCI_NUM_REGIONS
75+
#define MBOCHS_NUM_REGIONS (MBOCHS_EDID_REGION_INDEX+1)
76+
7477
#define MBOCHS_CONFIG_SPACE_SIZE 0xff
7578
#define MBOCHS_MMIO_BAR_OFFSET PAGE_SIZE
7679
#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 + \
7881
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)
7987

8088
#define STORE_LE16(addr, val) (*(u16 *)addr = val)
8189
#define STORE_LE32(addr, val) (*(u32 *)addr = val)
@@ -95,16 +103,24 @@ MODULE_PARM_DESC(mem, "megabytes available to " MBOCHS_NAME " devices");
95103
static const struct mbochs_type {
96104
const char *name;
97105
u32 mbytes;
106+
u32 max_x;
107+
u32 max_y;
98108
} mbochs_types[] = {
99109
{
100110
.name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_1,
101111
.mbytes = 4,
112+
.max_x = 800,
113+
.max_y = 600,
102114
}, {
103115
.name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_2,
104116
.mbytes = 16,
117+
.max_x = 1920,
118+
.max_y = 1440,
105119
}, {
106120
.name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_3,
107121
.mbytes = 64,
122+
.max_x = 0,
123+
.max_y = 0,
108124
},
109125
};
110126

@@ -115,6 +131,11 @@ static struct cdev mbochs_cdev;
115131
static struct device mbochs_dev;
116132
static int mbochs_used_mbytes;
117133

134+
struct vfio_region_info_ext {
135+
struct vfio_region_info base;
136+
struct vfio_region_info_cap_type type;
137+
};
138+
118139
struct mbochs_mode {
119140
u32 drm_format;
120141
u32 bytepp;
@@ -144,13 +165,14 @@ struct mdev_state {
144165
u32 memory_bar_mask;
145166
struct mutex ops_lock;
146167
struct mdev_device *mdev;
147-
struct vfio_device_info dev_info;
148168

149169
const struct mbochs_type *type;
150170
u16 vbe[VBE_DISPI_INDEX_COUNT];
151171
u64 memsize;
152172
struct page **pages;
153173
pgoff_t pagecount;
174+
struct vfio_region_gfx_edid edid_regs;
175+
u8 edid_blob[0x400];
154176

155177
struct list_head dmabufs;
156178
u32 active_id;
@@ -342,10 +364,20 @@ static void handle_mmio_read(struct mdev_state *mdev_state, u16 offset,
342364
char *buf, u32 count)
343365
{
344366
struct device *dev = mdev_dev(mdev_state->mdev);
367+
struct vfio_region_gfx_edid *edid;
345368
u16 reg16 = 0;
346369
int index;
347370

348371
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;
349381
case 0x500 ... 0x515: /* bochs dispi interface */
350382
if (count != 2)
351383
goto unhandled;
@@ -365,6 +397,44 @@ static void handle_mmio_read(struct mdev_state *mdev_state, u16 offset,
365397
}
366398
}
367399

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);
436+
}
437+
368438
static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count,
369439
loff_t pos, bool is_write)
370440
{
@@ -384,13 +454,25 @@ static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count,
384454
memcpy(buf, (mdev_state->vconfig + pos), count);
385455

386456
} 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)) {
388459
pos -= MBOCHS_MMIO_BAR_OFFSET;
389460
if (is_write)
390461
handle_mmio_write(mdev_state, pos, buf, count);
391462
else
392463
handle_mmio_read(mdev_state, pos, buf, count);
393464

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+
}
475+
394476
} else if (pos >= MBOCHS_MEMORY_BAR_OFFSET &&
395477
pos + count <=
396478
MBOCHS_MEMORY_BAR_OFFSET + mdev_state->memsize) {
@@ -471,6 +553,10 @@ static int mbochs_create(struct kobject *kobj, struct mdev_device *mdev)
471553
mdev_state->next_id = 1;
472554

473555
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);
474560
mbochs_create_config_space(mdev_state);
475561
mbochs_reset(mdev);
476562

@@ -932,16 +1018,16 @@ static int mbochs_dmabuf_export(struct mbochs_dmabuf *dmabuf)
9321018
}
9331019

9341020
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)
1021+
struct vfio_region_info_ext *ext)
9371022
{
1023+
struct vfio_region_info *region_info = &ext->base;
9381024
struct mdev_state *mdev_state;
9391025

9401026
mdev_state = mdev_get_drvdata(mdev);
9411027
if (!mdev_state)
9421028
return -EINVAL;
9431029

944-
if (region_info->index >= VFIO_PCI_NUM_REGIONS)
1030+
if (region_info->index >= MBOCHS_NUM_REGIONS)
9451031
return -EINVAL;
9461032

9471033
switch (region_info->index) {
@@ -964,6 +1050,20 @@ static int mbochs_get_region_info(struct mdev_device *mdev,
9641050
region_info->flags = (VFIO_REGION_INFO_FLAG_READ |
9651051
VFIO_REGION_INFO_FLAG_WRITE);
9661052
break;
1053+
case MBOCHS_EDID_REGION_INDEX:
1054+
ext->base.argsz = sizeof(*ext);
1055+
ext->base.offset = MBOCHS_EDID_OFFSET;
1056+
ext->base.size = MBOCHS_EDID_SIZE;
1057+
ext->base.flags = (VFIO_REGION_INFO_FLAG_READ |
1058+
VFIO_REGION_INFO_FLAG_WRITE |
1059+
VFIO_REGION_INFO_FLAG_CAPS);
1060+
ext->base.cap_offset = offsetof(typeof(*ext), type);
1061+
ext->type.header.id = VFIO_REGION_INFO_CAP_TYPE;
1062+
ext->type.header.version = 1;
1063+
ext->type.header.next = 0;
1064+
ext->type.type = VFIO_REGION_TYPE_GFX;
1065+
ext->type.subtype = VFIO_REGION_SUBTYPE_GFX_EDID;
1066+
break;
9671067
default:
9681068
region_info->size = 0;
9691069
region_info->offset = 0;
@@ -984,7 +1084,7 @@ static int mbochs_get_device_info(struct mdev_device *mdev,
9841084
struct vfio_device_info *dev_info)
9851085
{
9861086
dev_info->flags = VFIO_DEVICE_FLAGS_PCI;
987-
dev_info->num_regions = VFIO_PCI_NUM_REGIONS;
1087+
dev_info->num_regions = MBOCHS_NUM_REGIONS;
9881088
dev_info->num_irqs = VFIO_PCI_NUM_IRQS;
9891089
return 0;
9901090
}
@@ -1084,7 +1184,7 @@ static long mbochs_ioctl(struct mdev_device *mdev, unsigned int cmd,
10841184
unsigned long arg)
10851185
{
10861186
int ret = 0;
1087-
unsigned long minsz;
1187+
unsigned long minsz, outsz;
10881188
struct mdev_state *mdev_state;
10891189

10901190
mdev_state = mdev_get_drvdata(mdev);
@@ -1106,33 +1206,31 @@ static long mbochs_ioctl(struct mdev_device *mdev, unsigned int cmd,
11061206
if (ret)
11071207
return ret;
11081208

1109-
memcpy(&mdev_state->dev_info, &info, sizeof(info));
1110-
11111209
if (copy_to_user((void __user *)arg, &info, minsz))
11121210
return -EFAULT;
11131211

11141212
return 0;
11151213
}
11161214
case VFIO_DEVICE_GET_REGION_INFO:
11171215
{
1118-
struct vfio_region_info info;
1119-
u16 cap_type_id = 0;
1120-
void *cap_type = NULL;
1216+
struct vfio_region_info_ext info;
11211217

1122-
minsz = offsetofend(struct vfio_region_info, offset);
1218+
minsz = offsetofend(typeof(info), base.offset);
11231219

11241220
if (copy_from_user(&info, (void __user *)arg, minsz))
11251221
return -EFAULT;
11261222

1127-
if (info.argsz < minsz)
1223+
outsz = info.base.argsz;
1224+
if (outsz < minsz)
1225+
return -EINVAL;
1226+
if (outsz > sizeof(info))
11281227
return -EINVAL;
11291228

1130-
ret = mbochs_get_region_info(mdev, &info, &cap_type_id,
1131-
&cap_type);
1229+
ret = mbochs_get_region_info(mdev, &info);
11321230
if (ret)
11331231
return ret;
11341232

1135-
if (copy_to_user((void __user *)arg, &info, minsz))
1233+
if (copy_to_user((void __user *)arg, &info, outsz))
11361234
return -EFAULT;
11371235

11381236
return 0;
@@ -1148,7 +1246,7 @@ static long mbochs_ioctl(struct mdev_device *mdev, unsigned int cmd,
11481246
return -EFAULT;
11491247

11501248
if ((info.argsz < minsz) ||
1151-
(info.index >= mdev_state->dev_info.num_irqs))
1249+
(info.index >= VFIO_PCI_NUM_IRQS))
11521250
return -EINVAL;
11531251

11541252
ret = mbochs_get_irq_info(mdev, &info);

0 commit comments

Comments
 (0)