aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhao Yan <yan.y.zhao@intel.com>2018-06-19 03:44:11 -0400
committerZhenyu Wang <zhenyuw@linux.intel.com>2018-07-01 23:09:38 -0400
commit510fe10b6180137773dce9032b51bb82ff946c2d (patch)
tree30869b16502027fdf3729b878f49ee99ea6f629c
parent7a3727f385dc64773db1c144f6b15c1e9d4735bb (diff)
drm/i915/gvt: fix a bug of partially write ggtt enties
when guest writes ggtt entries, it could write 8 bytes a time if gtt_entry_size is 8. But, qemu could split the 8 bytes into 2 consecutive 4-byte writes. If each 4-byte partial write could trigger a host ggtt write, it is very possible that a wrong combination is written to the host ggtt. E.g. the higher 4 bytes is the old value, but the lower 4 bytes is the new value, and this 8-byte combination is wrong but written to the ggtt, thus causing bugs. To handle this condition, we just record the first 4-byte write, then wait until the second 4-byte write comes and write the combined 64-bit data to host ggtt table. To save memory space and to spot partial write as early as possible, we don't keep this information for every ggtt index. Instread, we just record the last ggtt write position, and assume the two 4-byte writes come in consecutively for each vgpu. This assumption is right based on the characteristic of ggtt entry which stores memory address. When gtt_entry_size is 8, the guest memory physical address should be 64 bits, so any sane guest driver should write 8-byte long data at a time, so 2 consecutive 4-byte writes at the same ggtt index should be trapped in gvt. v2: when incomplete ggtt entry write is located, e.g. 1. guest only writes 4 bytes at a ggtt offset and no long writes the rest 4 bytes. 2. guest writes 4 bytes of a ggtt offset, then write at other ggtt offsets, then return back to write the left 4 bytes of the first ggtt offset. add error handling logic to remap host entry to scratch page, and mark guest virtual ggtt entry as not present. (zhenyu wang) Signed-off-by: Zhao Yan <yan.y.zhao@intel.com> Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.c58
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.h2
2 files changed, 60 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c
index 23296547da95..4efec8fa6c1d 100644
--- a/drivers/gpu/drm/i915/gvt/gtt.c
+++ b/drivers/gpu/drm/i915/gvt/gtt.c
@@ -1592,6 +1592,7 @@ static struct intel_vgpu_mm *intel_vgpu_create_ggtt_mm(struct intel_vgpu *vgpu)
1592 vgpu_free_mm(mm); 1592 vgpu_free_mm(mm);
1593 return ERR_PTR(-ENOMEM); 1593 return ERR_PTR(-ENOMEM);
1594 } 1594 }
1595 mm->ggtt_mm.last_partial_off = -1UL;
1595 1596
1596 return mm; 1597 return mm;
1597} 1598}
@@ -1616,6 +1617,7 @@ void _intel_vgpu_mm_release(struct kref *mm_ref)
1616 invalidate_ppgtt_mm(mm); 1617 invalidate_ppgtt_mm(mm);
1617 } else { 1618 } else {
1618 vfree(mm->ggtt_mm.virtual_ggtt); 1619 vfree(mm->ggtt_mm.virtual_ggtt);
1620 mm->ggtt_mm.last_partial_off = -1UL;
1619 } 1621 }
1620 1622
1621 vgpu_free_mm(mm); 1623 vgpu_free_mm(mm);
@@ -1868,6 +1870,62 @@ static int emulate_ggtt_mmio_write(struct intel_vgpu *vgpu, unsigned int off,
1868 memcpy((void *)&e.val64 + (off & (info->gtt_entry_size - 1)), p_data, 1870 memcpy((void *)&e.val64 + (off & (info->gtt_entry_size - 1)), p_data,
1869 bytes); 1871 bytes);
1870 1872
1873 /* If ggtt entry size is 8 bytes, and it's split into two 4 bytes
1874 * write, we assume the two 4 bytes writes are consecutive.
1875 * Otherwise, we abort and report error
1876 */
1877 if (bytes < info->gtt_entry_size) {
1878 if (ggtt_mm->ggtt_mm.last_partial_off == -1UL) {
1879 /* the first partial part*/
1880 ggtt_mm->ggtt_mm.last_partial_off = off;
1881 ggtt_mm->ggtt_mm.last_partial_data = e.val64;
1882 return 0;
1883 } else if ((g_gtt_index ==
1884 (ggtt_mm->ggtt_mm.last_partial_off >>
1885 info->gtt_entry_size_shift)) &&
1886 (off != ggtt_mm->ggtt_mm.last_partial_off)) {
1887 /* the second partial part */
1888
1889 int last_off = ggtt_mm->ggtt_mm.last_partial_off &
1890 (info->gtt_entry_size - 1);
1891
1892 memcpy((void *)&e.val64 + last_off,
1893 (void *)&ggtt_mm->ggtt_mm.last_partial_data +
1894 last_off, bytes);
1895
1896 ggtt_mm->ggtt_mm.last_partial_off = -1UL;
1897 } else {
1898 int last_offset;
1899
1900 gvt_vgpu_err("failed to populate guest ggtt entry: abnormal ggtt entry write sequence, last_partial_off=%lx, offset=%x, bytes=%d, ggtt entry size=%d\n",
1901 ggtt_mm->ggtt_mm.last_partial_off, off,
1902 bytes, info->gtt_entry_size);
1903
1904 /* set host ggtt entry to scratch page and clear
1905 * virtual ggtt entry as not present for last
1906 * partially write offset
1907 */
1908 last_offset = ggtt_mm->ggtt_mm.last_partial_off &
1909 (~(info->gtt_entry_size - 1));
1910
1911 ggtt_get_host_entry(ggtt_mm, &m, last_offset);
1912 ggtt_invalidate_pte(vgpu, &m);
1913 ops->set_pfn(&m, gvt->gtt.scratch_mfn);
1914 ops->clear_present(&m);
1915 ggtt_set_host_entry(ggtt_mm, &m, last_offset);
1916 ggtt_invalidate(gvt->dev_priv);
1917
1918 ggtt_get_guest_entry(ggtt_mm, &e, last_offset);
1919 ops->clear_present(&e);
1920 ggtt_set_guest_entry(ggtt_mm, &e, last_offset);
1921
1922 ggtt_mm->ggtt_mm.last_partial_off = off;
1923 ggtt_mm->ggtt_mm.last_partial_data = e.val64;
1924
1925 return 0;
1926 }
1927 }
1928
1871 if (ops->test_present(&e)) { 1929 if (ops->test_present(&e)) {
1872 gfn = ops->get_pfn(&e); 1930 gfn = ops->get_pfn(&e);
1873 m = e; 1931 m = e;
diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h
index 3792f2b7f4ff..97e62647418a 100644
--- a/drivers/gpu/drm/i915/gvt/gtt.h
+++ b/drivers/gpu/drm/i915/gvt/gtt.h
@@ -150,6 +150,8 @@ struct intel_vgpu_mm {
150 } ppgtt_mm; 150 } ppgtt_mm;
151 struct { 151 struct {
152 void *virtual_ggtt; 152 void *virtual_ggtt;
153 unsigned long last_partial_off;
154 u64 last_partial_data;
153 } ggtt_mm; 155 } ggtt_mm;
154 }; 156 };
155}; 157};