Skip to content

Commit 39c68e8

Browse files
Hang Yuanzhenyw
authored andcommitted
drm/i915/gvt: add VFIO EDID region
Implement VFIO EDID region for vgpu. Support EDID blob update and notify guest on link state change via hotplug event. v3: move struct edid_region to kvmgt.c <zhenyu> v2: add EDID sanity check and size update <zhenyu> Tested-by: Gerd Hoffmann <kraxel@redhat.com> Reviewed-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Hang Yuan <hang.yuan@linux.intel.com> Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
1 parent 1ca20f3 commit 39c68e8

File tree

4 files changed

+167
-0
lines changed

4 files changed

+167
-0
lines changed

drivers/gpu/drm/i915/gvt/hypercall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct intel_gvt_mpt {
6767
int (*set_trap_area)(unsigned long handle, u64 start, u64 end,
6868
bool map);
6969
int (*set_opregion)(void *vgpu);
70+
int (*set_edid)(void *vgpu, int port_num);
7071
int (*get_vfio_device)(void *vgpu);
7172
void (*put_vfio_device)(void *vgpu);
7273
bool (*is_valid_gfn)(unsigned long handle, unsigned long gfn);

drivers/gpu/drm/i915/gvt/kvmgt.c

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ static const struct intel_gvt_ops *intel_gvt_ops;
5757
#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
5858
#define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
5959

60+
#define EDID_BLOB_OFFSET (PAGE_SIZE/2)
61+
6062
#define OPREGION_SIGNATURE "IntelGraphicsMem"
6163

6264
struct vfio_region;
@@ -76,6 +78,11 @@ struct vfio_region {
7678
void *data;
7779
};
7880

81+
struct vfio_edid_region {
82+
struct vfio_region_gfx_edid vfio_edid_regs;
83+
void *edid_blob;
84+
};
85+
7986
struct kvmgt_pgfn {
8087
gfn_t gfn;
8188
struct hlist_node hnode;
@@ -427,6 +434,111 @@ static const struct intel_vgpu_regops intel_vgpu_regops_opregion = {
427434
.release = intel_vgpu_reg_release_opregion,
428435
};
429436

437+
static int handle_edid_regs(struct intel_vgpu *vgpu,
438+
struct vfio_edid_region *region, char *buf,
439+
size_t count, u16 offset, bool is_write)
440+
{
441+
struct vfio_region_gfx_edid *regs = &region->vfio_edid_regs;
442+
unsigned int data;
443+
444+
if (offset + count > sizeof(*regs))
445+
return -EINVAL;
446+
447+
if (count != 4)
448+
return -EINVAL;
449+
450+
if (is_write) {
451+
data = *((unsigned int *)buf);
452+
switch (offset) {
453+
case offsetof(struct vfio_region_gfx_edid, link_state):
454+
if (data == VFIO_DEVICE_GFX_LINK_STATE_UP) {
455+
if (!drm_edid_block_valid(
456+
(u8 *)region->edid_blob,
457+
0,
458+
true,
459+
NULL)) {
460+
gvt_vgpu_err("invalid EDID blob\n");
461+
return -EINVAL;
462+
}
463+
intel_gvt_ops->emulate_hotplug(vgpu, true);
464+
} else if (data == VFIO_DEVICE_GFX_LINK_STATE_DOWN)
465+
intel_gvt_ops->emulate_hotplug(vgpu, false);
466+
else {
467+
gvt_vgpu_err("invalid EDID link state %d\n",
468+
regs->link_state);
469+
return -EINVAL;
470+
}
471+
regs->link_state = data;
472+
break;
473+
case offsetof(struct vfio_region_gfx_edid, edid_size):
474+
if (data > regs->edid_max_size) {
475+
gvt_vgpu_err("EDID size is bigger than %d!\n",
476+
regs->edid_max_size);
477+
return -EINVAL;
478+
}
479+
regs->edid_size = data;
480+
break;
481+
default:
482+
/* read-only regs */
483+
gvt_vgpu_err("write read-only EDID region at offset %d\n",
484+
offset);
485+
return -EPERM;
486+
}
487+
} else {
488+
memcpy(buf, (char *)regs + offset, count);
489+
}
490+
491+
return count;
492+
}
493+
494+
static int handle_edid_blob(struct vfio_edid_region *region, char *buf,
495+
size_t count, u16 offset, bool is_write)
496+
{
497+
if (offset + count > region->vfio_edid_regs.edid_size)
498+
return -EINVAL;
499+
500+
if (is_write)
501+
memcpy(region->edid_blob + offset, buf, count);
502+
else
503+
memcpy(buf, region->edid_blob + offset, count);
504+
505+
return count;
506+
}
507+
508+
static size_t intel_vgpu_reg_rw_edid(struct intel_vgpu *vgpu, char *buf,
509+
size_t count, loff_t *ppos, bool iswrite)
510+
{
511+
int ret;
512+
unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) -
513+
VFIO_PCI_NUM_REGIONS;
514+
struct vfio_edid_region *region =
515+
(struct vfio_edid_region *)vgpu->vdev.region[i].data;
516+
loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
517+
518+
if (pos < region->vfio_edid_regs.edid_offset) {
519+
ret = handle_edid_regs(vgpu, region, buf, count, pos, iswrite);
520+
} else {
521+
pos -= EDID_BLOB_OFFSET;
522+
ret = handle_edid_blob(region, buf, count, pos, iswrite);
523+
}
524+
525+
if (ret < 0)
526+
gvt_vgpu_err("failed to access EDID region\n");
527+
528+
return ret;
529+
}
530+
531+
static void intel_vgpu_reg_release_edid(struct intel_vgpu *vgpu,
532+
struct vfio_region *region)
533+
{
534+
kfree(region->data);
535+
}
536+
537+
static const struct intel_vgpu_regops intel_vgpu_regops_edid = {
538+
.rw = intel_vgpu_reg_rw_edid,
539+
.release = intel_vgpu_reg_release_edid,
540+
};
541+
430542
static int intel_vgpu_register_reg(struct intel_vgpu *vgpu,
431543
unsigned int type, unsigned int subtype,
432544
const struct intel_vgpu_regops *ops,
@@ -493,6 +605,36 @@ static int kvmgt_set_opregion(void *p_vgpu)
493605
return ret;
494606
}
495607

608+
static int kvmgt_set_edid(void *p_vgpu, int port_num)
609+
{
610+
struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;
611+
struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num);
612+
struct vfio_edid_region *base;
613+
int ret;
614+
615+
base = kzalloc(sizeof(*base), GFP_KERNEL);
616+
if (!base)
617+
return -ENOMEM;
618+
619+
/* TODO: Add multi-port and EDID extension block support */
620+
base->vfio_edid_regs.edid_offset = EDID_BLOB_OFFSET;
621+
base->vfio_edid_regs.edid_max_size = EDID_SIZE;
622+
base->vfio_edid_regs.edid_size = EDID_SIZE;
623+
base->vfio_edid_regs.max_xres = vgpu_edid_xres(port->id);
624+
base->vfio_edid_regs.max_yres = vgpu_edid_yres(port->id);
625+
base->edid_blob = port->edid->edid_block;
626+
627+
ret = intel_vgpu_register_reg(vgpu,
628+
VFIO_REGION_TYPE_GFX,
629+
VFIO_REGION_SUBTYPE_GFX_EDID,
630+
&intel_vgpu_regops_edid, EDID_SIZE,
631+
VFIO_REGION_INFO_FLAG_READ |
632+
VFIO_REGION_INFO_FLAG_WRITE |
633+
VFIO_REGION_INFO_FLAG_CAPS, base);
634+
635+
return ret;
636+
}
637+
496638
static void kvmgt_put_vfio_device(void *vgpu)
497639
{
498640
if (WARN_ON(!((struct intel_vgpu *)vgpu)->vdev.vfio_device))
@@ -1874,6 +2016,7 @@ static struct intel_gvt_mpt kvmgt_mpt = {
18742016
.dma_map_guest_page = kvmgt_dma_map_guest_page,
18752017
.dma_unmap_guest_page = kvmgt_dma_unmap_guest_page,
18762018
.set_opregion = kvmgt_set_opregion,
2019+
.set_edid = kvmgt_set_edid,
18772020
.get_vfio_device = kvmgt_get_vfio_device,
18782021
.put_vfio_device = kvmgt_put_vfio_device,
18792022
.is_valid_gfn = kvmgt_is_valid_gfn,

drivers/gpu/drm/i915/gvt/mpt.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,23 @@ static inline int intel_gvt_hypervisor_set_opregion(struct intel_vgpu *vgpu)
313313
return intel_gvt_host.mpt->set_opregion(vgpu);
314314
}
315315

316+
/**
317+
* intel_gvt_hypervisor_set_edid - Set EDID region for guest
318+
* @vgpu: a vGPU
319+
* @port_num: display port number
320+
*
321+
* Returns:
322+
* Zero on success, negative error code if failed.
323+
*/
324+
static inline int intel_gvt_hypervisor_set_edid(struct intel_vgpu *vgpu,
325+
int port_num)
326+
{
327+
if (!intel_gvt_host.mpt->set_edid)
328+
return 0;
329+
330+
return intel_gvt_host.mpt->set_edid(vgpu, port_num);
331+
}
332+
316333
/**
317334
* intel_gvt_hypervisor_get_vfio_device - increase vfio device ref count
318335
* @vgpu: a vGPU

drivers/gpu/drm/i915/gvt/vgpu.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,12 @@ static struct intel_vgpu *__intel_gvt_create_vgpu(struct intel_gvt *gvt,
428428
if (ret)
429429
goto out_clean_sched_policy;
430430

431+
/*TODO: add more platforms support */
432+
if (IS_SKYLAKE(gvt->dev_priv) || IS_KABYLAKE(gvt->dev_priv))
433+
ret = intel_gvt_hypervisor_set_edid(vgpu, PORT_D);
434+
if (ret)
435+
goto out_clean_sched_policy;
436+
431437
return vgpu;
432438

433439
out_clean_sched_policy:

0 commit comments

Comments
 (0)