@@ -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);
@@ -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
@@ -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_ */