[Intel-gfx] [RFC 16/29] drm/i915: gvt: Generic MPT framework

Zhi Wang zhi.a.wang at intel.com
Thu Jan 28 02:21:38 PST 2016


GVT-g supports both Xen/KVM hypervisors and requires a couple of hypervisor
services to work. The MPT framework is a kinds of abstraction which provides
a unique hypervisor APIs to GVT-g core logics.

Signed-off-by: Zhi Wang <zhi.a.wang at intel.com>
---
 drivers/gpu/drm/i915/gvt/gvt.c       |   6 ++
 drivers/gpu/drm/i915/gvt/gvt.h       |  11 +-
 drivers/gpu/drm/i915/gvt/hypercall.h |  26 +++++
 drivers/gpu/drm/i915/gvt/mmio.c      | 194 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/gvt/mmio.h      |   4 +
 drivers/gpu/drm/i915/gvt/mpt.h       | 103 +++++++++++++++----
 drivers/gpu/drm/i915/gvt/perf.h      |   4 +
 7 files changed, 326 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c
index 13fecdf..a71873c 100644
--- a/drivers/gpu/drm/i915/gvt/gvt.c
+++ b/drivers/gpu/drm/i915/gvt/gvt.c
@@ -31,6 +31,11 @@ struct gvt_host gvt_host;
 extern struct gvt_kernel_dm xengt_kdm;
 extern struct gvt_kernel_dm kvmgt_kdm;
 
+static struct gvt_io_emulation_ops default_io_emulation_ops = {
+	.emulate_mmio_read = gvt_emulate_mmio_read,
+	.emulate_mmio_write = gvt_emulate_mmio_write,
+};
+
 static const char *supported_hypervisors[] = {
 	[GVT_HYPERVISOR_TYPE_XEN] = "Xen Hypervisor",
 	[GVT_HYPERVISOR_TYPE_KVM] = "KVM",
@@ -72,6 +77,7 @@ static bool gvt_init_host(void)
 	gvt_info("Running with hypervisor %s in host mode",
 			supported_hypervisors[host->hypervisor_type]);
 
+	host->emulate_ops = &default_io_emulation_ops;
 	idr_init(&host->device_idr);
 	mutex_init(&host->device_idr_lock);
 
diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h
index 542f3e6..eb5fd47 100644
--- a/drivers/gpu/drm/i915/gvt/gvt.h
+++ b/drivers/gpu/drm/i915/gvt/gvt.h
@@ -31,7 +31,6 @@
 #include "params.h"
 #include "reg.h"
 #include "hypercall.h"
-#include "mpt.h"
 #include "fb_decoder.h"
 #include "mmio.h"
 #include "interrupt.h"
@@ -52,12 +51,20 @@ enum {
 	GVT_HYPERVISOR_TYPE_KVM,
 };
 
+struct gvt_io_emulation_ops {
+	bool (*emulate_mmio_read)(struct vgt_device *, uint64_t, void *, int);
+	bool (*emulate_mmio_write)(struct vgt_device *, uint64_t, void *, int);
+	bool (*emulate_cfg_read)(struct vgt_device *, unsigned int, void *, int);
+	bool (*emulate_cfg_write)(struct vgt_device *, unsigned int, void *, int);
+};
+
 struct gvt_host {
 	bool initialized;
 	int hypervisor_type;
 	struct mutex device_idr_lock;
 	struct idr device_idr;
 	struct gvt_kernel_dm *kdm;
+	struct gvt_io_emulation_ops *emulate_ops;
 };
 
 extern struct gvt_host gvt_host;
@@ -579,4 +586,6 @@ static inline u32 h2g_gtt_index(struct vgt_device *vgt, uint32_t h_index)
 	return (u32)(h2g_gm(vgt, h_addr) >> GTT_PAGE_SHIFT);
 }
 
+#include "mpt.h"
+
 #endif
diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h
index 0a41874..d30f5a7 100644
--- a/drivers/gpu/drm/i915/gvt/hypercall.h
+++ b/drivers/gpu/drm/i915/gvt/hypercall.h
@@ -24,7 +24,33 @@
 #ifndef _GVT_HYPERCALL_H_
 #define _GVT_HYPERCALL_H_
 
+struct vgt_device;
+struct guest_page;
+
+enum map_type {
+        GVT_MAP_APERTURE,
+        GVT_MAP_OPREGION,
+};
+
 struct gvt_kernel_dm {
+        const char *name;
+        unsigned long (*g2m_pfn)(int vm_id, unsigned long g_pfn);
+        int (*pause_domain)(int vm_id);
+        int (*shutdown_domain)(int vm_id);
+        int (*map_mfn_to_gpfn)(int vm_id, unsigned long gpfn,
+                unsigned long mfn, int nr, int map, enum map_type type);
+        int (*set_trap_area)(struct vgt_device *vgt, uint64_t start, uint64_t end, bool map);
+        bool (*set_wp_pages)(struct vgt_device *vgt, struct guest_page *p);
+        bool (*unset_wp_pages)(struct vgt_device *vgt, struct guest_page *p);
+        int (*detect_host)(void);
+        int (*from_virt_to_mfn)(void *addr);
+        void *(*from_mfn_to_virt)(int mfn);
+        int (*inject_msi)(int vm_id, u32 addr, u16 data);
+        int (*hvm_init)(struct vgt_device *vgt);
+        void (*hvm_exit)(struct vgt_device *vgt);
+        void *(*gpa_to_va)(struct vgt_device *vgt, unsigned long gap);
+        bool (*read_va)(struct vgt_device *vgt, void *va, void *val, int len, int atomic);
+        bool (*write_va)(struct vgt_device *vgt, void *va, void *val, int len, int atomic);
 };
 
 #endif /* _GVT_HYPERCALL_H_ */
diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c
index 28e1393..3297d82 100644
--- a/drivers/gpu/drm/i915/gvt/mmio.c
+++ b/drivers/gpu/drm/i915/gvt/mmio.c
@@ -320,3 +320,197 @@ void gvt_init_shadow_mmio_register(struct vgt_device *vgt)
 	struct gvt_virtual_device_state *state = &vgt->state;
         memcpy (state->mmio.sreg, vgt->pdev->initial_mmio_state, vgt->pdev->mmio_size);
 }
+
+unsigned int pa_to_mmio_offset(struct vgt_device *vgt,
+               uint64_t pa)
+{
+#define PCI_BAR_ADDR_MASK (~0xFUL)  /* 4 LSB bits are not address */
+       return pa - ((*(u64*)(vgt->state.cfg.space + GVT_REG_CFG_SPACE_BAR0))
+                       & PCI_BAR_ADDR_MASK);
+}
+
+static inline bool valid_mmio_alignment(struct gvt_mmio_entry *e,
+               unsigned int offset, int bytes)
+{
+       if ((bytes >= e->align_bytes) && !(offset & (bytes - 1)))
+               return true;
+       gvt_err("invalid MMIO offset %08x len %d", offset, bytes);
+       return false;
+}
+
+bool gvt_default_mmio_read(struct vgt_device *vgt, unsigned int offset,
+               void *p_data, unsigned int bytes)
+{
+       memcpy(p_data, (char *)vgt->state.mmio.vreg + offset, bytes);
+       return true;
+}
+
+bool gvt_default_mmio_write(struct vgt_device *vgt, unsigned int offset,
+               void *p_data, unsigned int bytes)
+{
+       memcpy((char *)vgt->state.mmio.vreg + offset, p_data, bytes);
+       return true;
+}
+
+bool gvt_emulate_mmio_read(struct vgt_device *vgt, uint64_t pa, void *p_data,int bytes)
+{
+       struct pgt_device *pdev = vgt->pdev;
+       struct gvt_statistics *stat = &vgt->stat;
+       struct gvt_mmio_entry *mmio_entry;
+       unsigned int offset;
+       cycles_t t0, t1;
+       bool r;
+
+       t0 = get_cycles();
+
+       mutex_lock(&pdev->lock);
+
+       if (atomic_read(&vgt->gtt.n_write_protected_guest_page)) {
+               guest_page_t *gp;
+               gp = gvt_find_guest_page(vgt, pa >> PAGE_SHIFT);
+               if (gp) {
+                       memcpy(p_data, gp->vaddr + (pa & ~PAGE_MASK), bytes);
+                       mutex_unlock(&pdev->lock);
+                       return true;
+               }
+       }
+
+       offset = pa_to_mmio_offset(vgt, pa);
+
+       if (bytes > 8 || (offset & (bytes - 1)))
+               goto err;
+
+       if (reg_is_gtt(pdev, offset)) {
+               r = gtt_emulate_read(vgt, offset, p_data, bytes);
+               mutex_unlock(&pdev->lock);
+               return r;
+       }
+
+       if (!reg_is_mmio(pdev, offset + bytes))
+               goto err;
+
+       mmio_entry = find_mmio_entry(pdev, offset);
+       if (mmio_entry && mmio_entry->read) {
+               if (!valid_mmio_alignment(mmio_entry, offset, bytes))
+                       goto err;
+               if (!mmio_entry->read(vgt, offset, p_data, bytes))
+                       goto err;
+       } else
+               if (!gvt_default_mmio_read(vgt, offset, p_data, bytes))
+                       goto err;
+
+       if (!reg_is_tracked(pdev, offset) && vgt->warn_untrack) {
+               gvt_warn("[ vgt%d ] untracked MMIO read, offset %x len %d val 0x%x",
+                       vgt->vm_id, offset, bytes, *(u32 *)p_data);
+
+               if (offset == 0x206c) {
+                       printk("------------------------------------------\n");
+                       printk("VM(%d) likely triggers a gfx reset\n", vgt->vm_id);
+                       printk("Disable untracked MMIO warning for VM(%d)\n", vgt->vm_id);
+                       printk("------------------------------------------\n");
+                       vgt->warn_untrack = 0;
+               }
+       }
+
+       reg_set_accessed(pdev, offset);
+       mutex_unlock(&pdev->lock);
+
+       t1 = get_cycles();
+       stat->mmio_rcnt++;
+       stat->mmio_rcycles += t1 - t0;
+       return true;
+err:
+       gvt_err("[ vgt%d ] fail to emulate MMIO read, offset %08x len %d",
+                       vgt->id, offset, bytes);
+       mutex_unlock(&pdev->lock);
+       return false;
+}
+
+bool gvt_emulate_mmio_write(struct vgt_device *vgt, uint64_t pa,
+       void *p_data, int bytes)
+{
+       struct pgt_device *pdev = vgt->pdev;
+       struct gvt_mmio_entry *mmio_entry;
+       struct gvt_statistics *stat = &vgt->stat;
+       unsigned int offset;
+       u32 old_vreg = 0, old_sreg = 0;
+       cycles_t t0, t1;
+       bool r;
+
+       t0 = get_cycles();
+
+       mutex_lock(&pdev->lock);
+
+       if (atomic_read(&vgt->gtt.n_write_protected_guest_page)) {
+               guest_page_t *guest_page;
+               guest_page = gvt_find_guest_page(vgt, pa >> PAGE_SHIFT);
+               if (guest_page) {
+                       r = guest_page->handler(guest_page, pa, p_data, bytes);
+                       t1 = get_cycles();
+                       stat->wp_cycles += t1 - t0;
+                       stat->wp_cnt++;
+                       mutex_unlock(&pdev->lock);
+                       return r;
+               }
+       }
+
+       offset = pa_to_mmio_offset(vgt, pa);
+
+       /* FENCE registers / GTT entries(sometimes) are accessed in 8 bytes. */
+       if (bytes > 8 || (offset & (bytes - 1)))
+               goto err;
+
+       if (reg_is_gtt(pdev, offset)) {
+               r = gtt_emulate_write(vgt, offset, p_data, bytes);
+               mutex_unlock(&pdev->lock);
+               return r;
+       }
+
+       if (!reg_is_mmio(pdev, offset + bytes))
+               goto err;
+
+       if (reg_mode_ctl(pdev, offset)) {
+               old_vreg = __vreg(vgt, offset);
+               old_sreg = __sreg(vgt, offset);
+       }
+
+       if (!reg_is_tracked(pdev, offset) && vgt->warn_untrack) {
+               gvt_warn("[ vgt%d ] untracked MMIO write, offset %x len %d val 0x%x",
+                       vgt->vm_id, offset, bytes, *(u32 *)p_data);
+       }
+
+       mmio_entry = find_mmio_entry(pdev, offset);
+       if (mmio_entry && mmio_entry->write ) {
+               if (!valid_mmio_alignment(mmio_entry, offset, bytes))
+                       goto err;
+               if (!mmio_entry->write(vgt, offset, p_data, bytes))
+                       goto err;
+       } else
+               if (!gvt_default_mmio_write(vgt, offset, p_data, bytes))
+                       goto err;
+
+       /* higher 16bits of mode ctl regs are mask bits for change */
+       if (reg_mode_ctl(pdev, offset)) {
+               u32 mask = __vreg(vgt, offset) >> 16;
+               /*
+                * share the global mask among VMs, since having one VM touch a bit
+                * not changed by another VM should be still saved/restored later
+                */
+               reg_aux_mode_mask(pdev, offset) |= mask << 16;
+               __vreg(vgt, offset) = (old_vreg & ~mask) | (__vreg(vgt, offset) & mask);
+               __sreg(vgt, offset) = (old_sreg & ~mask) | (__sreg(vgt, offset) & mask);
+       }
+
+       reg_set_accessed(pdev, offset);
+       mutex_unlock(&pdev->lock);
+
+       t1 = get_cycles();
+       stat->mmio_wcycles += t1 - t0;
+       stat->mmio_wcnt++;
+       return true;
+err:
+       gvt_err("[ vgt%d ] fail to emulate MMIO write, offset %08x len %d",
+                       vgt->id, offset, bytes);
+       mutex_unlock(&pdev->lock);
+       return false;
+}
diff --git a/drivers/gpu/drm/i915/gvt/mmio.h b/drivers/gpu/drm/i915/gvt/mmio.h
index caca60f..4301655 100644
--- a/drivers/gpu/drm/i915/gvt/mmio.h
+++ b/drivers/gpu/drm/i915/gvt/mmio.h
@@ -84,4 +84,8 @@ struct gvt_reg_info {
 extern struct gvt_reg_info gvt_general_reg_info[];
 extern struct gvt_reg_info gvt_broadwell_reg_info[];
 extern int gvt_get_reg_num(int type);
+
+bool gvt_emulate_mmio_read(struct vgt_device *vgt, uint64_t pa, void *p_data,int bytes);
+bool gvt_emulate_mmio_write(struct vgt_device *vgt, uint64_t pa, void *p_data,int bytes);
+
 #endif
diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h
index 99acf3d..f837dd1 100644
--- a/drivers/gpu/drm/i915/gvt/mpt.h
+++ b/drivers/gpu/drm/i915/gvt/mpt.h
@@ -24,85 +24,146 @@
 #ifndef _GVT_MPT_H_
 #define _GVT_MPT_H_
 
-struct guest_page;
-struct vgt_device;
-
 static inline unsigned long hypervisor_g2m_pfn(struct vgt_device *vgt,
 	unsigned long g_pfn)
 {
-	return 0;
+	return gvt_host.kdm->g2m_pfn(vgt->vm_id, g_pfn);
 }
 
 static inline int hypervisor_pause_domain(struct vgt_device *vgt)
 {
-	return 0;
+	return gvt_host.kdm->pause_domain(vgt->vm_id);
 }
 
 static inline int hypervisor_shutdown_domain(struct vgt_device *vgt)
 {
+	return gvt_host.kdm->shutdown_domain(vgt->vm_id);
+}
+
+static inline int hypervisor_map_mfn_to_gpfn(struct vgt_device *vgt,
+	unsigned long gpfn, unsigned long mfn, int nr, int map, enum map_type type)
+{
+	if (gvt_host.kdm && gvt_host.kdm->map_mfn_to_gpfn)
+		return gvt_host.kdm->map_mfn_to_gpfn(vgt->vm_id, gpfn, mfn, nr, map, type);
+
 	return 0;
 }
 
 static inline int hypervisor_set_trap_area(struct vgt_device *vgt,
-	uint64_t start, uint64_t end, bool map)
+	u64 start, u64 end, bool map)
 {
-	return 0;
+	return gvt_host.kdm->set_trap_area(vgt, start, end, map);
 }
 
-static inline bool hypervisor_detect_host(void)
+static inline int hypervisor_set_wp_pages(struct vgt_device *vgt, guest_page_t *p)
 {
-	return false;
+	return gvt_host.kdm->set_wp_pages(vgt, p);
 }
 
-static inline int hypervisor_virt_to_mfn(void *addr)
+static inline int hypervisor_unset_wp_pages(struct vgt_device *vgt, guest_page_t *p)
 {
-	return 0;
+	return gvt_host.kdm->unset_wp_pages(vgt, p);
 }
 
-static inline void *hypervisor_mfn_to_virt(int mfn)
+static inline int hypervisor_detect_host(void)
 {
-	return NULL;
+	return gvt_host.kdm->detect_host();
 }
 
-static inline int hypervisor_set_wp_pages(struct vgt_device *vgt, struct guest_page *p)
+static inline int hypervisor_virt_to_mfn(void *addr)
 {
-        return 0;
+	return gvt_host.kdm->from_virt_to_mfn(addr);
 }
 
-static inline int hypervisor_unset_wp_pages(struct vgt_device *vgt, struct guest_page *p)
+static inline void *hypervisor_mfn_to_virt(int mfn)
 {
-        return 0;
+	return gvt_host.kdm->from_mfn_to_virt(mfn);
 }
 
 static inline void hypervisor_inject_msi(struct vgt_device *vgt)
 {
-	return;
+#define MSI_CAP_OFFSET 0x90	/* FIXME. need to get from cfg emulation */
+#define MSI_CAP_CONTROL (MSI_CAP_OFFSET + 2)
+#define MSI_CAP_ADDRESS (MSI_CAP_OFFSET + 4)
+#define MSI_CAP_DATA	(MSI_CAP_OFFSET + 8)
+#define MSI_CAP_EN 0x1
+
+	char *cfg_space = &vgt->state.cfg.space[0];
+	u16 control = *(u16 *)(cfg_space + MSI_CAP_CONTROL);
+	u32 addr = *(u32 *)(cfg_space + MSI_CAP_ADDRESS);
+	u16 data = *(u16 *)(cfg_space + MSI_CAP_DATA);
+	int r;
+
+	/* Do not generate MSI if MSIEN is disable */
+	if (!(control & MSI_CAP_EN))
+		return;
+
+	/* FIXME: currently only handle one MSI format */
+	ASSERT_NUM(!(control & 0xfffe), control);
+
+	gvt_dbg(GVT_DBG_IRQ, "VM %d hvm injections. address (%x) data(%x)!",
+			vgt->vm_id, addr, data);
+	r = gvt_host.kdm->inject_msi(vgt->vm_id, addr, data);
+	if (r < 0)
+		gvt_err("VGT %d failed to inject vmsi", vgt->id);
 }
 
 static inline int hypervisor_hvm_init(struct vgt_device *vgt)
 {
+	if (gvt_host.kdm && gvt_host.kdm->hvm_init)
+		return gvt_host.kdm->hvm_init(vgt);
+
 	return 0;
 }
 
 static inline void hypervisor_hvm_exit(struct vgt_device *vgt)
 {
+	if (gvt_host.kdm && gvt_host.kdm->hvm_exit)
+		gvt_host.kdm->hvm_exit(vgt);
 }
 
 static inline void *hypervisor_gpa_to_va(struct vgt_device *vgt, unsigned long gpa)
 {
-	return NULL;
+	if (!vgt->vm_id)
+		return (char *)hypervisor_mfn_to_virt(gpa >> PAGE_SHIFT) + offset_in_page(gpa);
+
+	return gvt_host.kdm->gpa_to_va(vgt, gpa);
 }
 
 static inline bool hypervisor_read_va(struct vgt_device *vgt, void *va,
 		void *val, int len, int atomic)
 {
-	return false;
+	bool ret;
+
+	if (!vgt->vm_id) {
+		memcpy(val, va, len);
+		return true;
+	}
+
+	ret = gvt_host.kdm->read_va(vgt, va, val, len, atomic);
+	if (unlikely(!ret))
+		gvt_err("VM(%d): read va failed, va: 0x%p, atomic : %s\n", vgt->vm_id,
+				va, atomic ? "yes" : "no");
+
+	return ret;
 }
 
 static inline bool hypervisor_write_va(struct vgt_device *vgt, void *va,
 		void *val, int len, int atomic)
 {
-	return false;
+	bool ret;
+
+	if (!vgt->vm_id) {
+		memcpy(va, val, len);
+		return true;
+	}
+
+	ret = gvt_host.kdm->write_va(vgt, va, val, len, atomic);
+	if (unlikely(!ret))
+		gvt_err("VM(%d): write va failed, va: 0x%p, atomic : %s\n", vgt->vm_id,
+				va, atomic ? "yes" : "no");
+
+	return ret;
 }
 
 #endif /* _GVT_MPT_H_ */
diff --git a/drivers/gpu/drm/i915/gvt/perf.h b/drivers/gpu/drm/i915/gvt/perf.h
index 146a1cb..21b0637 100644
--- a/drivers/gpu/drm/i915/gvt/perf.h
+++ b/drivers/gpu/drm/i915/gvt/perf.h
@@ -28,6 +28,10 @@ struct gvt_statistics {
 	u64	irq_num;
 	u64	events[GVT_EVENT_MAX];
 	u64	last_injection;
+	u64	mmio_rcnt;
+	u64	mmio_wcnt;
+	u64	mmio_wcycles;
+	u64	mmio_rcycles;
 	u64	gtt_mmio_rcnt;
 	u64	gtt_mmio_wcnt;
 	u64	gtt_mmio_wcycles;
-- 
1.9.1



More information about the Intel-gfx mailing list