[PATCH 40/74] drm/i915: Implement vm_ops->access for gdb access into GTT mmaps.

Chris Wilson chris at chris-wilson.co.uk
Thu Sep 14 17:34:14 UTC 2017


gdb uses ptrace() to peek and poke bytes of the target's address space.
The driver must implement an vm_ops->access() handler or else gdb will
be unable to inspect the pointer and report it as out-of-bounds.
Worse than useless as it causes immediate suspicion of the valid GTT
pointer, distracting the poor programmer trying to find his bug.

Since we want that the access of the GTT mmap to be invisible to the
client, we try to minimise any state changes. Instead of binding the
object into the GGTT (causing gpu stalls, cache domain changes), we
simply kmap the backing storage and clflush to ensure coherency.
This limits us to only accessing objects backed by pages, which covers
all user objects for the time being.

Testcase: igt/gem_mmap_gtt/ptrace
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_drv.c |  1 +
 drivers/gpu/drm/i915/i915_drv.h |  2 ++
 drivers/gpu/drm/i915/i915_gem.c | 64 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 67 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 5c111ea96e80..90eca8a13c4e 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -2661,6 +2661,7 @@ const struct dev_pm_ops i915_pm_ops = {
 };
 
 static const struct vm_operations_struct i915_gem_vm_ops = {
+	.access = i915_gem_vm_access,
 	.fault = i915_gem_fault,
 	.open = drm_gem_vm_open,
 	.close = drm_gem_vm_close,
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index c7b2175e1592..bf117fbc0d30 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3699,6 +3699,8 @@ int i915_gem_wait_for_idle(struct drm_i915_private *dev_priv,
 int __must_check i915_gem_suspend(struct drm_i915_private *dev_priv);
 void i915_gem_resume(struct drm_i915_private *dev_priv);
 int i915_gem_fault(struct vm_fault *vmf);
+int i915_gem_vm_access(struct vm_area_struct *area, unsigned long addr,
+		       void *buf, int len, int write);
 int i915_gem_object_wait(struct drm_i915_gem_object *obj,
 			 unsigned int flags,
 			 long timeout,
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 6c40f2b0157e..ac4b2673578d 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2006,6 +2006,70 @@ static void __i915_gem_object_release_mmap(struct drm_i915_gem_object *obj)
 	}
 }
 
+/**
+ * i915_gem_vm_access - Peek and poke at the backing store, used by ptrace()
+ * @area: the obj being inspected by userspace
+ * @addr: the address within @area being accessed
+ * @buf: the user provided buffer to read from or write to
+ * @len: the number of bytes to read/write
+ * @write: the direction, if false we read from @buf, if true we write to @buf
+ *
+ * ptrace() has a private interface (vm_ops->access) through which it can
+ * access, or even modify, memory of its target process. This is used by gdb
+ * for inspecting that memory, without providing the access routine it will
+ * simply report such mmaps (our GTT mmaps) as out-of-bounds.
+ *
+ * For safety, we try to avoid perturbing state of the target object, beyond
+ * reason at least!
+ */
+int i915_gem_vm_access(struct vm_area_struct *area, unsigned long addr,
+		       void *buf, int len, int write)
+{
+	struct drm_i915_gem_object *obj = to_intel_bo(area->vm_private_data);
+	unsigned int offset;
+	unsigned long pfn;
+	int ret;
+
+	if (!i915_gem_object_has_struct_page(obj))
+		return -EINVAL;
+
+	ret = i915_gem_object_pin_pages(obj);
+	if (ret)
+		return ret;
+
+	offset = offset_in_page(addr - area->vm_start);
+	pfn = (addr - area->vm_start) >> PAGE_SHIFT;
+	while (len) {
+		unsigned int this = min_t(unsigned int, len, PAGE_SIZE);
+		struct page *page;
+		void *vaddr;
+
+		page = i915_gem_object_get_page(obj, pfn);
+		vaddr = kmap(page);
+		if (!(obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ))
+			drm_clflush_virt_range(vaddr + offset, this);
+		if (write)
+			memcpy(vaddr + offset, buf, this);
+		else
+			memcpy(buf, vaddr + offset, this);
+		if (!(obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_WRITE))
+			drm_clflush_virt_range(vaddr + offset, this);
+		kunmap(page);
+
+		buf += this;
+		len -= this;
+		ret += this;
+
+		offset = 0;
+		pfn++;
+	}
+
+	obj->mm.dirty |= write;
+	i915_gem_object_unpin_pages(obj);
+
+	return ret;
+}
+
 /**
  * i915_gem_release_mmap - remove physical page mappings
  * @obj: obj in question
-- 
2.14.1



More information about the Intel-gfx-trybot mailing list