[v1,4/4] drm/i915/gvt: add VFIO EDID region

Submitted by hang.yuan@linux.intel.com on Jan. 10, 2019, 11:04 a.m.

Details

Message ID 1547118288-1001-5-git-send-email-hang.yuan@linux.intel.com
State New
Series "drm/i915/gvt: add one VFIO graphics EDID region"
Headers show

Commit Message

hang.yuan@linux.intel.com Jan. 10, 2019, 11:04 a.m.
From: Hang Yuan <hang.yuan@linux.intel.com>

Create one EDID region in vgpu create and support the access to
this region.

Signed-off-by: Hang Yuan <hang.yuan@linux.intel.com>
---
 drivers/gpu/drm/i915/gvt/gvt.h       |   1 +
 drivers/gpu/drm/i915/gvt/hypercall.h |   1 +
 drivers/gpu/drm/i915/gvt/kvmgt.c     | 116 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/gvt/mpt.h       |  17 +++++
 drivers/gpu/drm/i915/gvt/vgpu.c      |   9 +++
 5 files changed, 144 insertions(+)

Patch hide | download patch | download mbox

diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h
index 0ba4b42..e0ec558 100644
--- a/drivers/gpu/drm/i915/gvt/gvt.h
+++ b/drivers/gpu/drm/i915/gvt/gvt.h
@@ -203,6 +203,7 @@  struct intel_vgpu {
 	struct {
 		struct mdev_device *mdev;
 		struct vfio_region *region;
+		struct vfio_region_gfx_edid vfio_edid_regs;
 		int num_regions;
 		struct eventfd_ctx *intx_trigger;
 		struct eventfd_ctx *msi_trigger;
diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h
index 2ab4138..98fe18b 100644
--- a/drivers/gpu/drm/i915/gvt/hypercall.h
+++ b/drivers/gpu/drm/i915/gvt/hypercall.h
@@ -67,6 +67,7 @@  struct intel_gvt_mpt {
 	int (*set_trap_area)(unsigned long handle, u64 start, u64 end,
 			     bool map);
 	int (*set_opregion)(void *vgpu);
+	int (*set_edid)(void *vgpu, int port_num);
 	void (*clean_regions)(void *vgpu);
 	int (*get_vfio_device)(void *vgpu);
 	void (*put_vfio_device)(void *vgpu);
diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c
index 8c30dc3..6796b75 100644
--- a/drivers/gpu/drm/i915/gvt/kvmgt.c
+++ b/drivers/gpu/drm/i915/gvt/kvmgt.c
@@ -57,6 +57,8 @@  static const struct intel_gvt_ops *intel_gvt_ops;
 #define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
 #define VFIO_PCI_OFFSET_MASK    (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
 
+#define EDID_BLOB_OFFSET (PAGE_SIZE/2)
+
 #define OPREGION_SIGNATURE "IntelGraphicsMem"
 
 struct vfio_region;
@@ -427,6 +429,89 @@  static const struct intel_vgpu_regops intel_vgpu_regops_opregion = {
 	.release = intel_vgpu_reg_release_opregion,
 };
 
+static int handle_edid_regs(struct intel_vgpu *vgpu, char *buf, size_t count,
+		u16 offset, bool is_write)
+{
+	struct vfio_region_gfx_edid *regs = (void *)&vgpu->vdev.vfio_edid_regs;
+
+	if (offset + count > sizeof(vgpu->vdev.vfio_edid_regs))
+		return -EINVAL;
+
+	if (count != 4)
+		return -EINVAL;
+
+	if (is_write) {
+		switch (offset) {
+		case offsetof(struct vfio_region_gfx_edid, link_state):
+			memcpy((char *)regs + offset, buf, count);
+			if (regs->link_state == VFIO_DEVICE_GFX_LINK_STATE_UP)
+				intel_gvt_ops->emulate_hotplug(vgpu, true);
+			else if (regs->link_state
+					== VFIO_DEVICE_GFX_LINK_STATE_DOWN)
+				intel_gvt_ops->emulate_hotplug(vgpu, false);
+			else
+				gvt_vgpu_err("invalid EDID link state %d\n",
+					regs->link_state);
+			break;
+		default:
+			/* read-only regs */
+			gvt_vgpu_err("write read-only EDID region at offset %d\n",
+				offset);
+		}
+	} else {
+		memcpy(buf, (char *)regs + offset, count);
+	}
+
+	return count;
+}
+
+static int handle_edid_blob(struct intel_vgpu *vgpu, char *base, char *buf,
+		size_t count, u16 offset, bool is_write)
+{
+	if (offset + count > vgpu->vdev.vfio_edid_regs.edid_size)
+		return -EINVAL;
+
+	if (is_write)
+		memcpy(base + offset, buf, count);
+	else
+		memcpy(buf, base + offset, count);
+
+	return count;
+}
+
+static size_t intel_vgpu_reg_rw_edid(struct intel_vgpu *vgpu, char *buf,
+		size_t count, loff_t *ppos, bool iswrite)
+{
+	int ret;
+	unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) -
+			VFIO_PCI_NUM_REGIONS;
+	void *base = vgpu->vdev.region[i].data;
+	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
+
+	if (pos < EDID_BLOB_OFFSET) {
+		ret = handle_edid_regs(vgpu, buf, count, pos, iswrite);
+	} else {
+		pos -= EDID_BLOB_OFFSET;
+		ret = handle_edid_blob(vgpu, base, buf, count, pos, iswrite);
+	}
+
+	if (ret < 0)
+		gvt_vgpu_err("failed to access EDID region at offset 0x%llx\n",
+				pos);
+
+	return ret;
+}
+
+static void intel_vgpu_reg_release_edid(struct intel_vgpu *vgpu,
+					struct vfio_region *region)
+{
+}
+
+static const struct intel_vgpu_regops intel_vgpu_regops_edid = {
+	.rw = intel_vgpu_reg_rw_edid,
+	.release = intel_vgpu_reg_release_edid,
+};
+
 static int intel_vgpu_register_reg(struct intel_vgpu *vgpu,
 		unsigned int type, unsigned int subtype,
 		const struct intel_vgpu_regops *ops,
@@ -493,6 +578,36 @@  static int kvmgt_set_opregion(void *p_vgpu)
 	return ret;
 }
 
+static int kvmgt_set_edid(void *p_vgpu, int port_num)
+{
+	struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;
+	struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num);
+	void *base;
+	int ret;
+
+	base = port->edid->edid_block;
+	if (!base)
+		return -ENOMEM;
+
+	vgpu->vdev.vfio_edid_regs.edid_offset = EDID_BLOB_OFFSET;
+
+	/* No extended EDID block for DP interface */
+	vgpu->vdev.vfio_edid_regs.edid_max_size = EDID_SIZE;
+	vgpu->vdev.vfio_edid_regs.edid_size = EDID_SIZE;
+	vgpu->vdev.vfio_edid_regs.max_xres = vgpu_edid_xres(port->id);
+	vgpu->vdev.vfio_edid_regs.max_yres = vgpu_edid_yres(port->id);
+
+	ret = intel_vgpu_register_reg(vgpu,
+			VFIO_REGION_TYPE_GFX,
+			VFIO_REGION_SUBTYPE_GFX_EDID,
+			&intel_vgpu_regops_edid, EDID_SIZE,
+			VFIO_REGION_INFO_FLAG_READ |
+			VFIO_REGION_INFO_FLAG_WRITE |
+			VFIO_REGION_INFO_FLAG_CAPS, base);
+
+	return ret;
+}
+
 static void kvmgt_clean_regions(void *p_vgpu)
 {
 	int i;
@@ -1888,6 +2003,7 @@  static struct intel_gvt_mpt kvmgt_mpt = {
 	.dma_map_guest_page = kvmgt_dma_map_guest_page,
 	.dma_unmap_guest_page = kvmgt_dma_unmap_guest_page,
 	.set_opregion = kvmgt_set_opregion,
+	.set_edid = kvmgt_set_edid,
 	.clean_regions = kvmgt_clean_regions,
 	.get_vfio_device = kvmgt_get_vfio_device,
 	.put_vfio_device = kvmgt_put_vfio_device,
diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h
index 1a07994..b675ed7 100644
--- a/drivers/gpu/drm/i915/gvt/mpt.h
+++ b/drivers/gpu/drm/i915/gvt/mpt.h
@@ -314,6 +314,23 @@  static inline int intel_gvt_hypervisor_set_opregion(struct intel_vgpu *vgpu)
 }
 
 /**
+  * intel_gvt_hypervisor_set_edid - Set EDID region for guest
+  * @vgpu: a vGPU
+  * @port_num: display port number
+  *
+  * Returns:
+  * Zero on success, negative error code if failed.
+  */
+static inline int intel_gvt_hypervisor_set_edid(struct intel_vgpu *vgpu,
+						int port_num)
+{
+	if (!intel_gvt_host.mpt->set_edid)
+		return 0;
+
+	return intel_gvt_host.mpt->set_edid(vgpu, port_num);
+}
+
+/**
  * intel_gvt_hypervisor_clean_regions - Clean regions for guest
  * @vgpu: a vGPU
  *
diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c
index c5eb565..2cec46f 100644
--- a/drivers/gpu/drm/i915/gvt/vgpu.c
+++ b/drivers/gpu/drm/i915/gvt/vgpu.c
@@ -429,8 +429,17 @@  static struct intel_vgpu *__intel_gvt_create_vgpu(struct intel_gvt *gvt,
 	if (ret)
 		goto out_clean_sched_policy;
 
+	/*TODO: add more platforms support */
+	if (IS_SKYLAKE(gvt->dev_priv) || IS_KABYLAKE(gvt->dev_priv))
+		ret = intel_gvt_hypervisor_set_edid(vgpu, PORT_D);
+
+	if (ret)
+		goto out_clean_regions;
+
 	return vgpu;
 
+out_clean_regions:
+	intel_gvt_hypervisor_clean_regions(vgpu);
 out_clean_sched_policy:
 	intel_vgpu_clean_sched_policy(vgpu);
 out_clean_submission:

Comments

Zhenyu Wang Jan. 14, 2019, 3:50 a.m.
On 2019.01.10 19:04:48 +0800, hang.yuan@linux.intel.com wrote:
> From: Hang Yuan <hang.yuan@linux.intel.com>
> 
> Create one EDID region in vgpu create and support the access to
> this region.
> 
> Signed-off-by: Hang Yuan <hang.yuan@linux.intel.com>
> ---
>  drivers/gpu/drm/i915/gvt/gvt.h       |   1 +
>  drivers/gpu/drm/i915/gvt/hypercall.h |   1 +
>  drivers/gpu/drm/i915/gvt/kvmgt.c     | 116 +++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/i915/gvt/mpt.h       |  17 +++++
>  drivers/gpu/drm/i915/gvt/vgpu.c      |   9 +++
>  5 files changed, 144 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h
> index 0ba4b42..e0ec558 100644
> --- a/drivers/gpu/drm/i915/gvt/gvt.h
> +++ b/drivers/gpu/drm/i915/gvt/gvt.h
> @@ -203,6 +203,7 @@ struct intel_vgpu {
>  	struct {
>  		struct mdev_device *mdev;
>  		struct vfio_region *region;
> +		struct vfio_region_gfx_edid vfio_edid_regs;
>  		int num_regions;
>  		struct eventfd_ctx *intx_trigger;
>  		struct eventfd_ctx *msi_trigger;
> diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h
> index 2ab4138..98fe18b 100644
> --- a/drivers/gpu/drm/i915/gvt/hypercall.h
> +++ b/drivers/gpu/drm/i915/gvt/hypercall.h
> @@ -67,6 +67,7 @@ struct intel_gvt_mpt {
>  	int (*set_trap_area)(unsigned long handle, u64 start, u64 end,
>  			     bool map);
>  	int (*set_opregion)(void *vgpu);
> +	int (*set_edid)(void *vgpu, int port_num);
>  	void (*clean_regions)(void *vgpu);
>  	int (*get_vfio_device)(void *vgpu);
>  	void (*put_vfio_device)(void *vgpu);
> diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c
> index 8c30dc3..6796b75 100644
> --- a/drivers/gpu/drm/i915/gvt/kvmgt.c
> +++ b/drivers/gpu/drm/i915/gvt/kvmgt.c
> @@ -57,6 +57,8 @@ static const struct intel_gvt_ops *intel_gvt_ops;
>  #define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
>  #define VFIO_PCI_OFFSET_MASK    (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
>  
> +#define EDID_BLOB_OFFSET (PAGE_SIZE/2)
> +
>  #define OPREGION_SIGNATURE "IntelGraphicsMem"
>  
>  struct vfio_region;
> @@ -427,6 +429,89 @@ static const struct intel_vgpu_regops intel_vgpu_regops_opregion = {
>  	.release = intel_vgpu_reg_release_opregion,
>  };
>  
> +static int handle_edid_regs(struct intel_vgpu *vgpu, char *buf, size_t count,
> +		u16 offset, bool is_write)
> +{
> +	struct vfio_region_gfx_edid *regs = (void *)&vgpu->vdev.vfio_edid_regs;
> +
> +	if (offset + count > sizeof(vgpu->vdev.vfio_edid_regs))
> +		return -EINVAL;
> +
> +	if (count != 4)
> +		return -EINVAL;
> +
> +	if (is_write) {
> +		switch (offset) {
> +		case offsetof(struct vfio_region_gfx_edid, link_state):
> +			memcpy((char *)regs + offset, buf, count);
> +			if (regs->link_state == VFIO_DEVICE_GFX_LINK_STATE_UP)
> +				intel_gvt_ops->emulate_hotplug(vgpu, true);
> +			else if (regs->link_state
> +					== VFIO_DEVICE_GFX_LINK_STATE_DOWN)
> +				intel_gvt_ops->emulate_hotplug(vgpu, false);
> +			else
> +				gvt_vgpu_err("invalid EDID link state %d\n",
> +					regs->link_state);

return error for invalid link state?

> +			break;
> +		default:
> +			/* read-only regs */
> +			gvt_vgpu_err("write read-only EDID region at offset %d\n",
> +				offset);

This should return error to write RO fields?

> +		}
> +	} else {
> +		memcpy(buf, (char *)regs + offset, count);
> +	}
> +
> +	return count;
> +}
> +
> +static int handle_edid_blob(struct intel_vgpu *vgpu, char *base, char *buf,
> +		size_t count, u16 offset, bool is_write)
> +{
> +	if (offset + count > vgpu->vdev.vfio_edid_regs.edid_size)
> +		return -EINVAL;
> +
> +	if (is_write)
> +		memcpy(base + offset, buf, count);
> +	else
> +		memcpy(buf, base + offset, count);
> +
> +	return count;
> +}
> +
> +static size_t intel_vgpu_reg_rw_edid(struct intel_vgpu *vgpu, char *buf,
> +		size_t count, loff_t *ppos, bool iswrite)
> +{
> +	int ret;
> +	unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) -
> +			VFIO_PCI_NUM_REGIONS;
> +	void *base = vgpu->vdev.region[i].data;
> +	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
> +
> +	if (pos < EDID_BLOB_OFFSET) {
> +		ret = handle_edid_regs(vgpu, buf, count, pos, iswrite);
> +	} else {
> +		pos -= EDID_BLOB_OFFSET;
> +		ret = handle_edid_blob(vgpu, base, buf, count, pos, iswrite);
> +	}
> +
> +	if (ret < 0)
> +		gvt_vgpu_err("failed to access EDID region at offset 0x%llx\n",
> +				pos);
> +
> +	return ret;
> +}
> +
> +static void intel_vgpu_reg_release_edid(struct intel_vgpu *vgpu,
> +					struct vfio_region *region)
> +{
> +}
> +
> +static const struct intel_vgpu_regops intel_vgpu_regops_edid = {
> +	.rw = intel_vgpu_reg_rw_edid,
> +	.release = intel_vgpu_reg_release_edid,
> +};
> +
>  static int intel_vgpu_register_reg(struct intel_vgpu *vgpu,
>  		unsigned int type, unsigned int subtype,
>  		const struct intel_vgpu_regops *ops,
> @@ -493,6 +578,36 @@ static int kvmgt_set_opregion(void *p_vgpu)
>  	return ret;
>  }
>  
> +static int kvmgt_set_edid(void *p_vgpu, int port_num)
> +{
> +	struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;
> +	struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num);
> +	void *base;
> +	int ret;
> +
> +	base = port->edid->edid_block;
> +	if (!base)
> +		return -ENOMEM;
> +
> +	vgpu->vdev.vfio_edid_regs.edid_offset = EDID_BLOB_OFFSET;
> +
> +	/* No extended EDID block for DP interface */
> +	vgpu->vdev.vfio_edid_regs.edid_max_size = EDID_SIZE;
> +	vgpu->vdev.vfio_edid_regs.edid_size = EDID_SIZE;
> +	vgpu->vdev.vfio_edid_regs.max_xres = vgpu_edid_xres(port->id);
> +	vgpu->vdev.vfio_edid_regs.max_yres = vgpu_edid_yres(port->id);
> +
> +	ret = intel_vgpu_register_reg(vgpu,
> +			VFIO_REGION_TYPE_GFX,
> +			VFIO_REGION_SUBTYPE_GFX_EDID,
> +			&intel_vgpu_regops_edid, EDID_SIZE,
> +			VFIO_REGION_INFO_FLAG_READ |
> +			VFIO_REGION_INFO_FLAG_WRITE |
> +			VFIO_REGION_INFO_FLAG_CAPS, base);
> +
> +	return ret;
> +}
> +
>  static void kvmgt_clean_regions(void *p_vgpu)
>  {
>  	int i;
> @@ -1888,6 +2003,7 @@ static struct intel_gvt_mpt kvmgt_mpt = {
>  	.dma_map_guest_page = kvmgt_dma_map_guest_page,
>  	.dma_unmap_guest_page = kvmgt_dma_unmap_guest_page,
>  	.set_opregion = kvmgt_set_opregion,
> +	.set_edid = kvmgt_set_edid,
>  	.clean_regions = kvmgt_clean_regions,
>  	.get_vfio_device = kvmgt_get_vfio_device,
>  	.put_vfio_device = kvmgt_put_vfio_device,
> diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h
> index 1a07994..b675ed7 100644
> --- a/drivers/gpu/drm/i915/gvt/mpt.h
> +++ b/drivers/gpu/drm/i915/gvt/mpt.h
> @@ -314,6 +314,23 @@ static inline int intel_gvt_hypervisor_set_opregion(struct intel_vgpu *vgpu)
>  }
>  
>  /**
> +  * intel_gvt_hypervisor_set_edid - Set EDID region for guest
> +  * @vgpu: a vGPU
> +  * @port_num: display port number
> +  *
> +  * Returns:
> +  * Zero on success, negative error code if failed.
> +  */
> +static inline int intel_gvt_hypervisor_set_edid(struct intel_vgpu *vgpu,
> +						int port_num)
> +{
> +	if (!intel_gvt_host.mpt->set_edid)
> +		return 0;
> +
> +	return intel_gvt_host.mpt->set_edid(vgpu, port_num);
> +}
> +
> +/**
>   * intel_gvt_hypervisor_clean_regions - Clean regions for guest
>   * @vgpu: a vGPU
>   *
> diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c
> index c5eb565..2cec46f 100644
> --- a/drivers/gpu/drm/i915/gvt/vgpu.c
> +++ b/drivers/gpu/drm/i915/gvt/vgpu.c
> @@ -429,8 +429,17 @@ static struct intel_vgpu *__intel_gvt_create_vgpu(struct intel_gvt *gvt,
>  	if (ret)
>  		goto out_clean_sched_policy;
>  
> +	/*TODO: add more platforms support */
> +	if (IS_SKYLAKE(gvt->dev_priv) || IS_KABYLAKE(gvt->dev_priv))
> +		ret = intel_gvt_hypervisor_set_edid(vgpu, PORT_D);
> +
> +	if (ret)
> +		goto out_clean_regions;
> +
>  	return vgpu;
>  
> +out_clean_regions:
> +	intel_gvt_hypervisor_clean_regions(vgpu);
>  out_clean_sched_policy:
>  	intel_vgpu_clean_sched_policy(vgpu);
>  out_clean_submission:
> -- 
> 2.7.4
> 
> _______________________________________________
> intel-gvt-dev mailing list
> intel-gvt-dev@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gvt-dev