diff mbox

[v3,1/2] drm/i915: read/write ioctls for userspace

Message ID 1301702089-25644-2-git-send-email-ben@bwidawsk.net (mailing list archive)
State New, archived
Headers show

Commit Message

Ben Widawsky April 1, 2011, 11:54 p.m. UTC
None
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 7273037..30cd576 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1846,6 +1846,174 @@  out_unlock:
 }
 EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable);
 
+static struct i915_register_range *
+get_register_range(struct drm_device *dev, u32 offset, int mode)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct i915_register_range *range = dev_priv->register_map.map;
+	u8 align = dev_priv->register_map.alignment_mask;
+	u32 range_count = dev_priv->register_map.length;
+
+	if (offset & dev_priv->register_map.alignment_mask)
+		return NULL;
+
+	if (offset >= dev_priv->register_map.top)
+		return NULL;
+
+	while(range_count--) {
+		/*  list is assumed to be in order */
+		if (offset < range->base)
+			break;
+
+		if ( (offset >= range->base) &&
+		     (offset + align) <= (range->base + range->size)) {
+			/*  assume perms ascend in security (ie. rw is > ro) */
+			if (mode > range->flags)
+				return NULL;
+			else
+				return range;
+		}
+		range++;
+	}
+
+	return NULL;
+}
+
+static int
+i915_read_register_ioctl(struct drm_device *dev, void *data,
+			 struct drm_file *file_priv)
+{
+	struct drm_intel_write_reg *args = data;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	if (args->rsvd != 0)
+		DRM_DEBUG("rsvd field should be zero\n");
+
+	if (get_register_range(dev, args->offset, I915_RANGE_RO) == NULL) {
+		args->value = 0xffffffff;
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	args->value = (u32)i915_gt_read(dev_priv, args->offset);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+static int
+i915_write_register_ioctl(struct drm_device *dev, void *data,
+			  struct drm_file *file_priv)
+{
+	struct drm_intel_read_reg *args = data;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct i915_register_range *range;
+
+	if (args->rsvd != 0)
+		DRM_DEBUG("rsvd field should be zero\n");
+
+	range = get_register_range(dev, args->offset, I915_RANGE_RW);
+	if (!range)
+		return -EINVAL;
+
+	mutex_lock(&dev->struct_mutex);
+	DRM_INFO("User space write %x %x\n", args->offset, (u32)args->value);
+	range->user_tainted = true;
+	i915_gt_write(dev_priv, args->offset, (u32)args->value);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+struct i915_register_range gen_bwcl_register_map[] = {
+	{0x00000000, 0x00000fff, I915_RANGE_RW},
+	{0x00001000, 0x00000fff, I915_RANGE_RSVD},
+	{0x00002000, 0x00000fff, I915_RANGE_RW},
+	{0x00003000, 0x000001ff, I915_RANGE_RW},
+	{0x00003200, 0x00000dff, I915_RANGE_RW},
+	{0x00004000, 0x000003ff, I915_RANGE_RSVD},
+	{0x00004400, 0x00000bff, I915_RANGE_RSVD},
+	{0x00005000, 0x00000fff, I915_RANGE_RW},
+	{0x00006000, 0x00000fff, I915_RANGE_RW},
+	{0x00007000, 0x000003ff, I915_RANGE_RW},
+	{0x00007400, 0x000014ff, I915_RANGE_RW},
+	{0x00008900, 0x000006ff, I915_RANGE_RSVD},
+	{0x00009000, 0x00000fff, I915_RANGE_RSVD},
+	{0x0000a000, 0x00000fff, I915_RANGE_RW},
+	{0x0000b000, 0x00004fff, I915_RANGE_RSVD},
+	{0x00010000, 0x00003fff, I915_RANGE_RW},
+	{0x00014000, 0x0001bfff, I915_RANGE_RSVD},
+	{0x00030000, 0x0000ffff, I915_RANGE_RW},
+	{0x00040000, 0x0001ffff, I915_RANGE_RSVD},
+	{0x00060000, 0x0000ffff, I915_RANGE_RW},
+	{0x00070000, 0x00002fff, I915_RANGE_RW},
+	{0x00073000, 0x00000fff, I915_RANGE_RW},
+	{0x00074000, 0x0000bfff, I915_RANGE_RSVD}
+};
+
+struct i915_register_range genx_register_map[] = {
+	{0x00000000, 0x00000fff, I915_RANGE_RW},
+	{0x00001000, 0x00000fff, I915_RANGE_RSVD},
+	{0x00002000, 0x00000fff, I915_RANGE_RW},
+	{0x00003000, 0x000001ff, I915_RANGE_RW},
+	{0x00003200, 0x00000dff, I915_RANGE_RW},
+	{0x00004000, 0x000003ff, I915_RANGE_RW},
+	{0x00004400, 0x00000bff, I915_RANGE_RW},
+	{0x00005000, 0x00000fff, I915_RANGE_RW},
+	{0x00006000, 0x00000fff, I915_RANGE_RW},
+	{0x00007000, 0x000003ff, I915_RANGE_RW},
+	{0x00007400, 0x000014ff, I915_RANGE_RW},
+	{0x00008900, 0x000006ff, I915_RANGE_RSVD},
+	{0x00009000, 0x00000fff, I915_RANGE_RSVD},
+	{0x0000a000, 0x00000fff, I915_RANGE_RW},
+	{0x0000b000, 0x00004fff, I915_RANGE_RSVD},
+	{0x00010000, 0x00003fff, I915_RANGE_RW},
+	{0x00014000, 0x0001bfff, I915_RANGE_RSVD},
+	{0x00030000, 0x0000ffff, I915_RANGE_RW},
+	{0x00040000, 0x0001ffff, I915_RANGE_RSVD},
+	{0x00060000, 0x0000ffff, I915_RANGE_RW},
+	{0x00070000, 0x00002fff, I915_RANGE_RW},
+	{0x00073000, 0x00000fff, I915_RANGE_RW},
+	{0x00074000, 0x0000bfff, I915_RANGE_RSVD}
+};
+
+struct i915_register_range gen6_gt_register_map[] = {
+	{0x00000000, 0x00000fff, I915_RANGE_RW},
+	{0x00001000, 0x00000fff, I915_RANGE_RSVD},
+	{0x00002000, 0x00000fff, I915_RANGE_RW},
+	{0x00003000, 0x000001ff, I915_RANGE_RW},
+	{0x00003200, 0x00000dff, I915_RANGE_RW},
+	{0x00004000, 0x00000fff, I915_RANGE_RW},
+	{0x00005000, 0x0000017f, I915_RANGE_RW},
+	{0x00005180, 0x00000e7f, I915_RANGE_RW},
+	{0x00006000, 0x00001fff, I915_RANGE_RW},
+	{0x00008000, 0x000007ff, I915_RANGE_RW},
+	{0x00008800, 0x000000ff, I915_RANGE_RSVD},
+	{0x00008900, 0x000006ff, I915_RANGE_RW},
+	{0x00009000, 0x00000fff, I915_RANGE_RSVD},
+	{0x0000a000, 0x00000fff, I915_RANGE_RW},
+	{0x0000b000, 0x00004fff, I915_RANGE_RSVD},
+	{0x00010000, 0x00001fff, I915_RANGE_RW},
+	{0x00012000, 0x000003ff, I915_RANGE_RW},
+	{0x00012400, 0x00000bff, I915_RANGE_RW},
+	{0x00013000, 0x00000fff, I915_RANGE_RW},
+	{0x00014000, 0x00000fff, I915_RANGE_RW},
+	{0x00015000, 0x0000cfff, I915_RANGE_RW},
+	{0x00022000, 0x00000fff, I915_RANGE_RW},
+	{0x00023000, 0x00000fff, I915_RANGE_RSVD},
+	{0x00024000, 0x00000fff, I915_RANGE_RW},
+	{0x00025000, 0x0000afff, I915_RANGE_RSVD},
+	{0x00030000, 0x0000ffff, I915_RANGE_RW},
+	{0x00040000, 0x0001ffff, I915_RANGE_RSVD},
+	{0x00060000, 0x0000ffff, I915_RANGE_RW},
+	{0x00070000, 0x00002fff, I915_RANGE_RW},
+	{0x00073000, 0x00000fff, I915_RANGE_RW},
+	{0x00074000, 0x0008bfff, I915_RANGE_RSVD},
+	{0x00100000, 0x00007fff, I915_RANGE_RW},
+	{0x00108000, 0x00037fff, I915_RANGE_RSVD},
+	{0x00140000, 0x0003ffff, I915_RANGE_RW}
+};
+
 /**
  * Tells the intel_ips driver that the i915 driver is now loaded, if
  * IPS got loaded first.
@@ -2062,6 +2230,25 @@  int i915_driver_load(struct drm_device *dev, unsigned long flags)
 
 	ips_ping_for_i915_load();
 
+	if (IS_GEN6(dev)) {
+		dev_priv->register_map.map = gen6_gt_register_map;
+		dev_priv->register_map.length =
+			ARRAY_SIZE(gen6_gt_register_map);
+		dev_priv->register_map.top = 0x180000;
+	} else if (IS_BROADWATER(dev) || IS_CRESTLINE(dev)) {
+		dev_priv->register_map.map = gen_bwcl_register_map;
+		dev_priv->register_map.length =
+			ARRAY_SIZE(gen_bwcl_register_map);
+		dev_priv->register_map.top = 0x80000;
+	} else {
+		dev_priv->register_map.map = genx_register_map;
+		dev_priv->register_map.length =
+			ARRAY_SIZE(genx_register_map);
+		dev_priv->register_map.top = 0x80000;
+	}
+
+	dev_priv->register_map.alignment_mask = 0x3;
+
 	return 0;
 
 out_gem_unload:
@@ -2276,6 +2463,8 @@  struct drm_ioctl_desc i915_ioctls[] = {
 	DRM_IOCTL_DEF_DRV(I915_GEM_MADVISE, i915_gem_madvise_ioctl, DRM_UNLOCKED),
 	DRM_IOCTL_DEF_DRV(I915_OVERLAY_PUT_IMAGE, intel_overlay_put_image, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 	DRM_IOCTL_DEF_DRV(I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+	DRM_IOCTL_DEF_DRV(I915_READ_REGISTER, i915_read_register_ioctl, DRM_UNLOCKED),
+	DRM_IOCTL_DEF_DRV(I915_WRITE_REGISTER, i915_write_register_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED),
 };
 
 int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 5004724..355f008 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -703,6 +703,14 @@  typedef struct drm_i915_private {
 	struct intel_fbdev *fbdev;
 
 	struct drm_property *broadcast_rgb_property;
+
+	/* User visible register map */
+	struct {
+		struct i915_register_range *map;
+		u32 length;
+		u32 top;
+		u8 alignment_mask;
+	} register_map;
 } drm_i915_private_t;
 
 struct drm_i915_gem_object {
@@ -1385,4 +1393,17 @@  static inline void i915_gt_write(struct drm_i915_private *dev_priv,
 		__gen6_gt_wait_for_fifo(dev_priv);
 	I915_WRITE(reg, val);
 }
+
+/* Register range interface */
+struct i915_register_range {
+	u32 base;
+	u32 size;
+	u8 flags;
+	bool user_tainted;
+};
+
+#define I915_RANGE_RSVD	(0<<0)
+#define I915_RANGE_RO	(1<<0)
+#define I915_RANGE_RW	(1<<1)
+
 #endif
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index c4d6dbf..9398cb2 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -198,6 +198,8 @@  typedef struct _drm_i915_sarea {
 #define DRM_I915_OVERLAY_PUT_IMAGE	0x27
 #define DRM_I915_OVERLAY_ATTRS	0x28
 #define DRM_I915_GEM_EXECBUFFER2	0x29
+#define DRM_I915_READ_REGISTER	0x2a
+#define DRM_I915_WRITE_REGISTER	0x2b
 
 #define DRM_IOCTL_I915_INIT		DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
 #define DRM_IOCTL_I915_FLUSH		DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -239,6 +241,8 @@  typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GEM_MADVISE	DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
 #define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE	DRM_IOW(DRM_COMMAND_BASE + DRM_IOCTL_I915_OVERLAY_ATTRS, struct drm_intel_overlay_put_image)
 #define DRM_IOCTL_I915_OVERLAY_ATTRS	DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs)
+#define DRM_IOCTL_I915_READ_REGISTER	DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_READ_REGISTER, struct drm_intel_read_reg)
+#define DRM_IOCTL_I915_WRITE_REGISTER	DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_WRITE_REGISTER, struct drm_intel_write_reg)
 
 /* Allow drivers to submit batchbuffers directly to hardware, relying
  * on the security mechanisms provided by hardware.
@@ -844,4 +848,30 @@  struct drm_intel_overlay_attrs {
 	__u32 gamma5;
 };
 
+struct drm_intel_read_reg {
+	/* register offset to read */
+	__u32 offset;
+
+	/* register size, RFU */
+	__u8 size;
+
+	/* return value, high 4 bytes RFU */
+	__u64 value;
+
+	__u64 rsvd;
+};
+
+struct drm_intel_write_reg {
+	/* register offset to read */
+	__u32 offset;
+
+	/* register size,  RFU */
+	__u8 size;
+
+	/* value to write, high 4 bytes RFU */
+	__u64 value;
+
+	__u64 rsvd;
+};
+
 #endif				/* _I915_DRM_H_ */