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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxxxxxx>
---
 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 d310d8245dca..acd0c8b86f9d 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -2652,6 +2652,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 81cd21ecfa7d..74e836690207 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3504,6 +3504,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 1b2dfa8bdeef..580b5042f4f7 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.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/intel-gfx




[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux