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

Chris Wilson chris at chris-wilson.co.uk
Wed Jul 26 11:50:35 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 214555e813f1..957fb9d33ec9 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -2651,6 +2651,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 2c7456f4ed38..e4bb10750a4c 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3548,6 +3548,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 6faabf34f142..5cc2bec74546 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2009,6 +2009,70 @@ int i915_gem_fault(struct vm_fault *vmf)
 }
 
 /**
+ * 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)
+			drm_clflush_virt_range(vaddr + offset, this);
+		if (write)
+			memcpy(vaddr + offset, buf, this);
+		else
+			memcpy(buf, vaddr + offset, this);
+		if (!obj->cache_coherent)
+			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.13.3



More information about the Intel-gfx-trybot mailing list