* [PATCH] drm/i915: read/write IOCTLs @ 2011-04-01 1:31 Ben Widawsky 2011-04-01 1:39 ` [PATCH v2] " Ben Widawsky 2011-04-01 6:36 ` [PATCH] drm/i915: read/write IOCTLs Chris Wilson 0 siblings, 2 replies; 19+ messages in thread From: Ben Widawsky @ 2011-04-01 1:31 UTC (permalink / raw) To: intel-gfx With the invention of forcewake (DevGT) , userspace tools to diagnose problems are no longer reliable. This will provide userspace a mechanism to read registers through an IOCTL, as well as root permission to write to registers. The code tries to be smart about which registers can be read and written, as hardware behavior may become erratic if writing to reserved ranges. Signed-off-by: Ben Widawsky <ben@bwidawsk.net> --- drivers/gpu/drm/i915/i915_dma.c | 129 +++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 2 + include/drm/i915_drm.h | 27 ++++++++ 3 files changed, 158 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 7273037..8b5165d 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1846,6 +1846,133 @@ out_unlock: } EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable); + +/* If we assume registers are dword aligned, second check is redundant */ +#define REG_IN_RANGE(reg, begin, end) \ + ((reg <= end && reg >= begin) || \ + ((reg+3) <= end && (reg+3) >= begin)) + +/* + * This is here to avoid hanging the GPU from userspace. + * The ranges are defined the in "Register Address Map" section of the + * documents. + */ +static inline int +reg_in_rsvd_range(struct drm_device *dev, u32 reg) +{ + if (INTEL_INFO(dev)->gen < 6) { + if (REG_IN_RANGE(reg, 0x1000, 0x1fff) || + ((IS_BROADWATER(dev) || IS_CRESTLINE(dev)) ? + REG_IN_RANGE(reg, 0x4000, 0x4fff) : 0) || + REG_IN_RANGE(reg, 0x9000, 0x9fff) || + REG_IN_RANGE(reg, 0xb000, 0xffff) || + REG_IN_RANGE(reg, 0x14000, 0x2ffff) || + REG_IN_RANGE(reg, 0x40000, 0x5ffff)) + return 1; + + if (reg > 0x73fff) + return 1; + } else { + if (REG_IN_RANGE(reg, 0x1000, 0x1fff) || + REG_IN_RANGE(reg, 0x9000, 0x9fff) || + REG_IN_RANGE(reg, 0xb000, 0xffff) || + REG_IN_RANGE(reg, 0x15000, 0x21fff) || + REG_IN_RANGE(reg, 0x23000, 0x23fff) || + REG_IN_RANGE(reg, 0x25000, 0x2ffff) || + REG_IN_RANGE(reg, 0x40000, 0xfffff) || + REG_IN_RANGE(reg, 0x108000, 0x13ffff)) + return 1; + + if (reg > 0x17ffff) + return 1; + } + + return 0; +} + + +#define REG_IN_READ_BLACKLIST(reg) 0 +#define REG_IN_WRITE_BLACKLIST(reg) 0 + +static inline int +reg_read_allowed(struct drm_device *dev, u32 offset) +{ + if (offset & 0x3) + return 0; + + if (reg_in_rsvd_range(dev, offset)) + return 0; + + if (REG_IN_READ_BLACKLIST(offset)) + return 0; + + return 1; +} + +static inline int +reg_write_allowed(struct drm_device *dev, u32 offset) +{ + if (offset & 0x3) + return 0; + + if (reg_in_rsvd_range(dev, offset)) + return 0; + + if (REG_IN_WRITE_BLACKLIST(offset)) + return 0; + + return 1; +} + +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; + int ret; + + if (!reg_read_allowed(dev, args->offset)) { + args->value = 0xffffffff; + return -EINVAL; + } + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + DRM_DEBUG_DRIVER("Couldn't acquire mutex, reading anyway\n"); + + 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; + int ret; + + if (!reg_write_allowed(dev, args->offset)) + return -EINVAL; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + DRM_DEBUG_DRIVER("Couldn't acquire mutex, writing anyway\n"); + + DRM_INFO("User space write %x %x\n", args->offset, (u32)args->value); + dev_priv->user_tainted = true; + + i915_gt_write(dev_priv, args->offset, (u32)args->value); + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + /** * Tells the intel_ips driver that the i915 driver is now loaded, if * IPS got loaded first. @@ -2276,6 +2403,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..4c66734 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -703,6 +703,8 @@ typedef struct drm_i915_private { struct intel_fbdev *fbdev; struct drm_property *broadcast_rgb_property; + + bool user_tainted; } drm_i915_private_t; struct drm_i915_gem_object { diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index c4d6dbf..68eeaa3 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,27 @@ 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; +}; + +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; + +}; + #endif /* _I915_DRM_H_ */ -- 1.7.3.4 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v2] drm/i915: read/write IOCTLs 2011-04-01 1:31 [PATCH] drm/i915: read/write IOCTLs Ben Widawsky @ 2011-04-01 1:39 ` Ben Widawsky 2011-04-01 23:54 ` Read/Write IOCTL compromise Ben Widawsky ` (2 more replies) 2011-04-01 6:36 ` [PATCH] drm/i915: read/write IOCTLs Chris Wilson 1 sibling, 3 replies; 19+ messages in thread From: Ben Widawsky @ 2011-04-01 1:39 UTC (permalink / raw) To: intel-gfx With the invention of forcewake (DevGT) , userspace tools to diagnose problems are no longer reliable. This will provide userspace a mechanism to read registers through an IOCTL, as well as root permission to write to registers. The code tries to be smart about which registers can be read and written, as hardware behavior may become erratic if writing to reserved ranges. Signed-off-by: Ben Widawsky <ben@bwidawsk.net> --- drivers/gpu/drm/i915/i915_dma.c | 130 +++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 2 + include/drm/i915_drm.h | 27 ++++++++ 3 files changed, 159 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 7273037..a96602e 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1846,6 +1846,134 @@ out_unlock: } EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable); + +/* If we assume registers are dword aligned, second check is redundant */ +#define REG_IN_RANGE(reg, begin, end) \ + ((reg <= end && reg >= begin) || \ + ((reg+3) <= end && (reg+3) >= begin)) + +/* + * This is here to avoid hanging the GPU from userspace. + * The ranges are defined the in "Register Address Map" section of the + * documents. + */ +static inline int +reg_in_rsvd_range(struct drm_device *dev, u32 reg) +{ + if (INTEL_INFO(dev)->gen < 6) { + if (REG_IN_RANGE(reg, 0x1000, 0x1fff) || + ((IS_BROADWATER(dev) || IS_CRESTLINE(dev)) ? + REG_IN_RANGE(reg, 0x4000, 0x4fff) : 0) || + REG_IN_RANGE(reg, 0x9000, 0x9fff) || + REG_IN_RANGE(reg, 0xb000, 0xffff) || + REG_IN_RANGE(reg, 0x14000, 0x2ffff) || + REG_IN_RANGE(reg, 0x40000, 0x5ffff)) + return 1; + + if (reg > 0x73fff) + return 1; + } else { + if (REG_IN_RANGE(reg, 0x1000, 0x1fff) || + REG_IN_RANGE(reg, 0x8800, 0x88ff) || + REG_IN_RANGE(reg, 0x9000, 0x9fff) || + REG_IN_RANGE(reg, 0xb000, 0xffff) || + REG_IN_RANGE(reg, 0x15000, 0x21fff) || + REG_IN_RANGE(reg, 0x23000, 0x23fff) || + REG_IN_RANGE(reg, 0x25000, 0x2ffff) || + REG_IN_RANGE(reg, 0x40000, 0xfffff) || + REG_IN_RANGE(reg, 0x108000, 0x13ffff)) + return 1; + + if (reg > 0x17ffff) + return 1; + } + + return 0; +} + + +#define REG_IN_READ_BLACKLIST(reg) 0 +#define REG_IN_WRITE_BLACKLIST(reg) 0 + +static inline int +reg_read_allowed(struct drm_device *dev, u32 offset) +{ + if (offset & 0x3) + return 0; + + if (reg_in_rsvd_range(dev, offset)) + return 0; + + if (REG_IN_READ_BLACKLIST(offset)) + return 0; + + return 1; +} + +static inline int +reg_write_allowed(struct drm_device *dev, u32 offset) +{ + if (offset & 0x3) + return 0; + + if (reg_in_rsvd_range(dev, offset)) + return 0; + + if (REG_IN_WRITE_BLACKLIST(offset)) + return 0; + + return 1; +} + +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; + int ret; + + if (!reg_read_allowed(dev, args->offset)) { + args->value = 0xffffffff; + return -EINVAL; + } + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + DRM_DEBUG_DRIVER("Couldn't acquire mutex, reading anyway\n"); + + 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; + int ret; + + if (!reg_write_allowed(dev, args->offset)) + return -EINVAL; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + DRM_DEBUG_DRIVER("Couldn't acquire mutex, writing anyway\n"); + + DRM_INFO("User space write %x %x\n", args->offset, (u32)args->value); + dev_priv->user_tainted = true; + + i915_gt_write(dev_priv, args->offset, (u32)args->value); + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + /** * Tells the intel_ips driver that the i915 driver is now loaded, if * IPS got loaded first. @@ -2276,6 +2404,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..4c66734 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -703,6 +703,8 @@ typedef struct drm_i915_private { struct intel_fbdev *fbdev; struct drm_property *broadcast_rgb_property; + + bool user_tainted; } drm_i915_private_t; struct drm_i915_gem_object { diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index c4d6dbf..68eeaa3 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,27 @@ 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; +}; + +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; + +}; + #endif /* _I915_DRM_H_ */ -- 1.7.3.4 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Read/Write IOCTL compromise 2011-04-01 1:39 ` [PATCH v2] " Ben Widawsky @ 2011-04-01 23:54 ` Ben Widawsky 2011-04-06 21:38 ` No more read/write ioctls Ben Widawsky 2011-04-01 23:54 ` [PATCH v3 1/2] drm/i915: read/write ioctls for userspace Ben Widawsky 2011-04-01 23:54 ` [PATCH v3 2/2] drm/i915: debugfs for register write taint Ben Widawsky 2 siblings, 1 reply; 19+ messages in thread From: Ben Widawsky @ 2011-04-01 23:54 UTC (permalink / raw) To: intel-gfx This patch does the 1 register read/write at a time, with a rsvd field for more ambitious stuff later. In locking, it does the safe mutex_lock/unlock. We can always modify this later if we see a need. So if this isn't okay by everyone, let's get down to the minimum number of changes required to get this accepted so I can move on to the tools, and then get back to the debug stuff. Ben ^ permalink raw reply [flat|nested] 19+ messages in thread
* No more read/write ioctls 2011-04-01 23:54 ` Read/Write IOCTL compromise Ben Widawsky @ 2011-04-06 21:38 ` Ben Widawsky 2011-04-08 17:10 ` Eric Anholt 0 siblings, 1 reply; 19+ messages in thread From: Ben Widawsky @ 2011-04-06 21:38 UTC (permalink / raw) To: intel-gfx On Fri, Apr 01, 2011 at 04:54:47PM -0700, Ben Widawsky wrote: > So if this isn't okay by everyone, let's get down to the minimum number > of changes required to get this accepted so I can move on to the tools, > and then get back to the debug stuff. > Some discussion on IRC has led to a new proposal (well 2 new proposals, but the first one wasn't viable). The interface will instead of using ioctls use debugfs. The debugfs file will control force wake. There will be a refcount mechanism for number of users of the registers in the relevant power-well, and upon opening a specific file in debugfs (we could have one per power-well if needed), the refcount will get incremented, and decremented at close. In other words, for userspace to read/write registers: fd = open(/sys/kernel/debug/dri...) normal read write mechanism close(fd) There are two side effects which everyone on IRC seems fine with: * root-only read access (the ioctl read was promiscuous) * access is only available when debugfs is mounted As a result, you should ignore both the gpu-tools patches, as well as these kernel patches. Thanks. Ben ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: No more read/write ioctls 2011-04-06 21:38 ` No more read/write ioctls Ben Widawsky @ 2011-04-08 17:10 ` Eric Anholt 2011-04-08 17:29 ` Chris Wilson 0 siblings, 1 reply; 19+ messages in thread From: Eric Anholt @ 2011-04-08 17:10 UTC (permalink / raw) To: Ben Widawsky, intel-gfx [-- Attachment #1.1: Type: text/plain, Size: 1379 bytes --] On Wed, 6 Apr 2011 14:38:27 -0700, Ben Widawsky <ben@bwidawsk.net> wrote: > On Fri, Apr 01, 2011 at 04:54:47PM -0700, Ben Widawsky wrote: > > > So if this isn't okay by everyone, let's get down to the minimum number > > of changes required to get this accepted so I can move on to the tools, > > and then get back to the debug stuff. > > > > Some discussion on IRC has led to a new proposal (well 2 new proposals, > but the first one wasn't viable). > > The interface will instead of using ioctls use debugfs. The debugfs file > will control force wake. There will be a refcount mechanism for number > of users of the registers in the relevant power-well, and upon opening a > specific file in debugfs (we could have one per power-well if needed), > the refcount will get incremented, and decremented at close. > > In other words, for userspace to read/write registers: > fd = open(/sys/kernel/debug/dri...) > normal read write mechanism > close(fd) > > There are two side effects which everyone on IRC seems fine with: > * root-only read access (the ioctl read was promiscuous) > * access is only available when debugfs is mounted > > As a result, you should ignore both the gpu-tools patches, as well as > these kernel patches. I thought previously we had issues with debugfs nodes which could wedge the system (out of range mmio reads)? [-- Attachment #1.2: Type: application/pgp-signature, Size: 197 bytes --] [-- Attachment #2: Type: text/plain, Size: 159 bytes --] _______________________________________________ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/intel-gfx ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: No more read/write ioctls 2011-04-08 17:10 ` Eric Anholt @ 2011-04-08 17:29 ` Chris Wilson 0 siblings, 0 replies; 19+ messages in thread From: Chris Wilson @ 2011-04-08 17:29 UTC (permalink / raw) To: Eric Anholt, Ben Widawsky, intel-gfx On Fri, 08 Apr 2011 07:10:14 -1000, Eric Anholt <eric@anholt.net> wrote: > I thought previously we had issues with debugfs nodes which could wedge > the system (out of range mmio reads)? Yes, we have indeed triggered hard hards by trying to read the wrong registers before. That's why I was interested in seeing Ben codify the reserved ranges so that we can have at least a warning before doing something idiotic. -Chris -- Chris Wilson, Intel Open Source Technology Centre ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v3 1/2] drm/i915: read/write ioctls for userspace 2011-04-01 1:39 ` [PATCH v2] " Ben Widawsky 2011-04-01 23:54 ` Read/Write IOCTL compromise Ben Widawsky @ 2011-04-01 23:54 ` Ben Widawsky 2011-04-04 17:14 ` Chris Wilson 2011-04-01 23:54 ` [PATCH v3 2/2] drm/i915: debugfs for register write taint Ben Widawsky 2 siblings, 1 reply; 19+ messages in thread From: Ben Widawsky @ 2011-04-01 23:54 UTC (permalink / raw) To: intel-gfx Straight forward IOCTLs which allow userspace to read and write registers. This is needed because on newer generation products, registers may become inaccessible due to specific power modes. To manage this, created a set of register ranges for the different products which can give fine grained control over what we permit userspace to read and write. This list was created by hand, so expect issues/bugs. The fields in the calls allow register widths to be defined, although currently this is not used by any products. There is also a rsvd field which can be used for giving a list of register reads/writes to perform in the future. Signed-off-by: Ben Widawsky <ben@bwidawsk.net> --- drivers/gpu/drm/i915/i915_dma.c | 189 +++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 21 +++++ include/drm/i915_drm.h | 30 ++++++ 3 files changed, 240 insertions(+), 0 deletions(-) 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_ */ -- 1.7.3.4 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH v3 1/2] drm/i915: read/write ioctls for userspace 2011-04-01 23:54 ` [PATCH v3 1/2] drm/i915: read/write ioctls for userspace Ben Widawsky @ 2011-04-04 17:14 ` Chris Wilson 2011-04-05 1:30 ` Read/Write ioctls Ben Widawsky 2011-04-05 1:30 ` [PATCH v4] drm/i915: read/write ioctls for userspace Ben Widawsky 0 siblings, 2 replies; 19+ messages in thread From: Chris Wilson @ 2011-04-04 17:14 UTC (permalink / raw) To: Ben Widawsky, intel-gfx Comments inline. On Fri, 1 Apr 2011 16:54:48 -0700, Ben Widawsky <ben@bwidawsk.net> wrote: > Straight forward IOCTLs which allow userspace to read and write > registers. This is needed because on newer generation products, > registers may become inaccessible due to specific power modes. > > To manage this, created a set of register ranges for the different > products which can give fine grained control over what we permit > userspace to read and write. This list was created by hand, so expect > issues/bugs. > > The fields in the calls allow register widths to be defined, although > currently this is not used by any products. There is also a rsvd field > which can be used for giving a list of register reads/writes to perform > in the future. > > Signed-off-by: Ben Widawsky <ben@bwidawsk.net> > --- > drivers/gpu/drm/i915/i915_dma.c | 189 +++++++++++++++++++++++++++++++++++++++ > drivers/gpu/drm/i915/i915_drv.h | 21 +++++ > include/drm/i915_drm.h | 30 ++++++ > 3 files changed, 240 insertions(+), 0 deletions(-) > > 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--) { Missingwhitespace.;-) Rather than range_count/dev_priv->register_map.length, I would prefer a sentinel. > + /* 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"); No, don't even mention it, just ignore it. > + > + if (get_register_range(dev, args->offset, I915_RANGE_RO) == NULL) { > + args->value = 0xffffffff; > + return -EINVAL; > + } > + > + mutex_lock(&dev->struct_mutex); Will be happier with i915_mutex_lock_interruptible() just in case. > + args->value = (u32)i915_gt_read(dev_priv, args->offset); And we need to start doing intel_register_read_get(dev, args->offset); switch (args->size) { switch 8: args->value = ioread8(regs+args->offset); break; ... } intel_register_read_put(dev, args->offset); where intel_register_read_get() looks something like int intel_register_read_get(struct drm_device *dev, int offset) { switch (INTEL_INFO(dev)->gen) { case 6: if (offset < 0x40000) __gen6_gt_force_wake_get(dev); return 0; case 5: case 4: case 3: return 0; } } > + 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); ret = intel_register_write_get() if (ret == 0) { switch (args->size) { case 8: iowrite8(args->value, regs+args->offset); break; } intel_register_write_put() } > + mutex_unlock(&dev->struct_mutex); > + > + return 0; > +} > + > +struct i915_register_range gen_bwcl_register_map[] = { static const struct i915_register_range ... Keep sparse quiet. > + {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; Wow, didn't expect the gen5 register map to match gen2. > + 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; As noted we can remove this. I hope. > + 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; Save a puppy, merge this into flags. > +}; > + > +#define I915_RANGE_RSVD (0<<0) > +#define I915_RANGE_RO (1<<0) > +#define I915_RANGE_RW (1<<1) Mind renaming this as I915_RANGE_READ, I915_RANGE_WRITE and use them as distinct permission bits and then I915_RANGE_RW is (I915_RANGE_READ | I915_RANGE_WRITE). Yes, there are the occasional write-only registers out there. > + > #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; Make this a _u32 for alignment's sake. Leave a note saying that are a spare 26 bits if you want ;-) > + > + /* 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_ */ > -- > 1.7.3.4 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/intel-gfx -- Chris Wilson, Intel Open Source Technology Centre ^ permalink raw reply [flat|nested] 19+ messages in thread
* Read/Write ioctls 2011-04-04 17:14 ` Chris Wilson @ 2011-04-05 1:30 ` Ben Widawsky 2011-04-05 1:30 ` [PATCH v4] drm/i915: read/write ioctls for userspace Ben Widawsky 1 sibling, 0 replies; 19+ messages in thread From: Ben Widawsky @ 2011-04-05 1:30 UTC (permalink / raw) To: intel-gfx Went back to 1 patch instead of 2... Two things from your comments not addressed (which I think should be addressed separately): * < gen4 register maps (userspace tools will fall back to old method) * intel_register_write_get - I think the whole register read/write situation needs cleanup, and I'd like to look at those changes in isolation from this change. Ben ^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH v4] drm/i915: read/write ioctls for userspace 2011-04-04 17:14 ` Chris Wilson 2011-04-05 1:30 ` Read/Write ioctls Ben Widawsky @ 2011-04-05 1:30 ` Ben Widawsky 1 sibling, 0 replies; 19+ messages in thread From: Ben Widawsky @ 2011-04-05 1:30 UTC (permalink / raw) To: intel-gfx [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 15453 bytes --] Straight forward IOCTLs which allow userspace to read and write registers. This is needed because on newer generation products, registers may become inaccessible due to specific power modes. To manage this, created a set of register ranges for the different products which can give fine grained control over what we permit userspace to read and write. This list was created by hand, so expect issues/bugs. The fields in the calls allow register widths to be defined, although currently this is not used by any products. There is also a rsvd field which can be used for giving a list of register reads/writes to perform in the future. Signed-off-by: Ben Widawsky <ben@bwidawsk.net> --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/i915_debugfs.c | 19 +++++ drivers/gpu/drm/i915/i915_dma.c | 87 ++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 25 ++++++ drivers/gpu/drm/i915/i915_reg_maps.c | 143 ++++++++++++++++++++++++++++++++++ include/drm/i915_drm.h | 30 +++++++ 6 files changed, 305 insertions(+), 0 deletions(-) create mode 100644 drivers/gpu/drm/i915/i915_reg_maps.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 0ae6a7c..6544076 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -13,6 +13,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \ i915_gem_gtt.o \ i915_gem_tiling.o \ i915_trace_points.o \ + i915_reg_maps.o \ intel_display.o \ intel_crt.o \ intel_lvds.o \ diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 87c8e29..81f49d0 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1186,6 +1186,24 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) return 0; } +static int i915_user_tainted_info(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_register_range *range = dev_priv->register_map.map; + + while (!(range->flags & I915_RANGE_END)) { + if (range->flags & I915_RANGE_TAINTED) { + seq_printf(m, "tainted register range 0x%08x->0x%08x\n", + range->base, range->size); + } + range++; + } + + return 0; +} + static int i915_wedged_open(struct inode *inode, struct file *filp) @@ -1324,6 +1342,7 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_sr_status", i915_sr_status, 0}, {"i915_opregion", i915_opregion, 0}, {"i915_gem_framebuffer", i915_gem_framebuffer_info, 0}, + {"i915_user_tainted", i915_user_tainted_info, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 7273037..cce61a8 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1846,6 +1846,89 @@ 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; + + if (offset & dev_priv->register_map.alignment_mask) + return NULL; + + if (offset >= dev_priv->register_map.top) + return NULL; + + while (!(range->flags & I915_RANGE_END)) { + /* list is assumed to be in order */ + if (offset < range->base) + break; + + if ( (offset >= range->base) && + (offset + align) <= (range->base + range->size)) { + if ((mode & range->flags) == mode) + 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; + int ret; + + if (dev_priv->register_map.map == NULL) + return -ENOTSUPP; + + if (get_register_range(dev, args->offset, I915_RANGE_READ) == NULL) { + args->value = 0xffffffff; + return -EINVAL; + } + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + 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; + int ret; + + if (dev_priv->register_map.map == NULL) + return -ENOTSUPP; + + range = get_register_range(dev, args->offset, I915_RANGE_WRITE); + if (!range) + return -EINVAL; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + DRM_INFO("User space write %x %x\n", args->offset, (u32)args->value); + range->flags |= I915_RANGE_TAINTED; + i915_gt_write(dev_priv, args->offset, (u32)args->value); + mutex_unlock(&dev->struct_mutex); + + return 0; +} + /** * Tells the intel_ips driver that the i915 driver is now loaded, if * IPS got loaded first. @@ -2062,6 +2145,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ips_ping_for_i915_load(); + i915_init_register_map(dev); + return 0; out_gem_unload: @@ -2276,6 +2361,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..ab48a1b 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,21 @@ 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; + u32 flags; /* protected by struct_mutex */ +}; + +#define I915_RANGE_RSVD (0<<0) /* Shouldn't be read or written */ +#define I915_RANGE_READ (1<<0) +#define I915_RANGE_WRITE (1<<1) +#define I915_RANGE_RW (I915_RANGE_READ | I915_RANGE_WRITE) +#define I915_RANGE_TAINTED (1<<30) +#define I915_RANGE_END (1<<31) + +extern int i915_init_register_map(struct drm_device *dev); + #endif diff --git a/drivers/gpu/drm/i915/i915_reg_maps.c b/drivers/gpu/drm/i915/i915_reg_maps.c new file mode 100644 index 0000000..21f008d --- /dev/null +++ b/drivers/gpu/drm/i915/i915_reg_maps.c @@ -0,0 +1,143 @@ +/* + * Copyright © 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Ben Widawsky <ben@bwidawsk.net> + * + */ + +#include "i915_drv.h" + +static 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}, + {0x00000000, 0x00000000, I915_RANGE_END} +}; + +static struct i915_register_range gen4_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}, + {0x00000000, 0x00000000, I915_RANGE_END} +}; + +/* The documentation is a little sketchy on these register ranges. */ +static 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}, + {0x00000000, 0x00000000, I915_RANGE_END} +}; + +int i915_init_register_map(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (IS_GEN6(dev)) { + dev_priv->register_map.map = 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.top = 0x80000; + } else if (INTEL_INFO(dev)->gen >= 4) { + dev_priv->register_map.map = gen4_register_map; + dev_priv->register_map.top = 0x80000; + } else { + dev_priv->register_map.map = NULL;; + } + + dev_priv->register_map.alignment_mask = 0x3; + + return 0; +} diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index c4d6dbf..ff136af 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 */ + __u32 size; + + /* return value, high 4 bytes RFU */ + __u64 value; + + __u64 rsvd; +}; + +struct drm_intel_write_reg { + /* register offset to read */ + __u32 offset; + + /* register size */ + __u32 size; + + /* value to write, high 4 bytes RFU */ + __u64 value; + + __u64 rsvd; +}; + #endif /* _I915_DRM_H_ */ -- 1.7.3.4 [-- Attachment #2: Type: text/plain, Size: 159 bytes --] _______________________________________________ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/intel-gfx ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH v3 2/2] drm/i915: debugfs for register write taint 2011-04-01 1:39 ` [PATCH v2] " Ben Widawsky 2011-04-01 23:54 ` Read/Write IOCTL compromise Ben Widawsky 2011-04-01 23:54 ` [PATCH v3 1/2] drm/i915: read/write ioctls for userspace Ben Widawsky @ 2011-04-01 23:54 ` Ben Widawsky 2 siblings, 0 replies; 19+ messages in thread From: Ben Widawsky @ 2011-04-01 23:54 UTC (permalink / raw) To: intel-gfx Provide an interface to see which register ranges have been tainted by the user's interaction. Signed-off-by: Ben Widawsky <ben@bwidawsk.net> --- drivers/gpu/drm/i915/i915_debugfs.c | 20 ++++++++++++++++++++ 1 files changed, 20 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 87c8e29..7142a0c 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1186,6 +1186,25 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) return 0; } +static int i915_user_tainted_info(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_register_range *range = dev_priv->register_map.map; + u32 range_count = dev_priv->register_map.length; + + while(range_count--) { + if (range->user_tainted) { + seq_printf(m, "tainted register range 0x%08x->0x%08x\n", + range->base, range->size); + } + range++; + } + + return 0; +} + static int i915_wedged_open(struct inode *inode, struct file *filp) @@ -1324,6 +1343,7 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_sr_status", i915_sr_status, 0}, {"i915_opregion", i915_opregion, 0}, {"i915_gem_framebuffer", i915_gem_framebuffer_info, 0}, + {"i915_user_tainted", i915_user_tainted_info, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) -- 1.7.3.4 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH] drm/i915: read/write IOCTLs 2011-04-01 1:31 [PATCH] drm/i915: read/write IOCTLs Ben Widawsky 2011-04-01 1:39 ` [PATCH v2] " Ben Widawsky @ 2011-04-01 6:36 ` Chris Wilson 2011-04-01 7:06 ` Ben Widawsky 1 sibling, 1 reply; 19+ messages in thread From: Chris Wilson @ 2011-04-01 6:36 UTC (permalink / raw) To: Ben Widawsky, intel-gfx Nice. The consensus is that this ioctl is required, just a few comments inline. On Thu, 31 Mar 2011 18:31:49 -0700, Ben Widawsky <ben@bwidawsk.net> wrote: > With the invention of forcewake (DevGT) , userspace tools to diagnose > problems are no longer reliable. This will provide userspace a mechanism > to read registers through an IOCTL, as well as root permission to write > to registers. > > The code tries to be smart about which registers can be read and > written, as hardware behavior may become erratic if writing to reserved > ranges. > > Signed-off-by: Ben Widawsky <ben@bwidawsk.net> > --- > drivers/gpu/drm/i915/i915_dma.c | 129 +++++++++++++++++++++++++++++++++++++++ > drivers/gpu/drm/i915/i915_drv.h | 2 + > include/drm/i915_drm.h | 27 ++++++++ > 3 files changed, 158 insertions(+), 0 deletions(-) > > diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c > index 7273037..8b5165d 100644 > --- a/drivers/gpu/drm/i915/i915_dma.c > +++ b/drivers/gpu/drm/i915/i915_dma.c > @@ -1846,6 +1846,133 @@ out_unlock: > } > EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable); > > + > +/* If we assume registers are dword aligned, second check is redundant */ > +#define REG_IN_RANGE(reg, begin, end) \ > + ((reg <= end && reg >= begin) || \ > + ((reg+3) <= end && (reg+3) >= begin)) > + > +/* > + * This is here to avoid hanging the GPU from userspace. > + * The ranges are defined the in "Register Address Map" section of the > + * documents. > + */ > +static inline int > +reg_in_rsvd_range(struct drm_device *dev, u32 reg) > +{ > + if (INTEL_INFO(dev)->gen < 6) { > + if (REG_IN_RANGE(reg, 0x1000, 0x1fff) || > + ((IS_BROADWATER(dev) || IS_CRESTLINE(dev)) ? > + REG_IN_RANGE(reg, 0x4000, 0x4fff) : 0) || > + REG_IN_RANGE(reg, 0x9000, 0x9fff) || > + REG_IN_RANGE(reg, 0xb000, 0xffff) || > + REG_IN_RANGE(reg, 0x14000, 0x2ffff) || > + REG_IN_RANGE(reg, 0x40000, 0x5ffff)) > + return 1; > + > + if (reg > 0x73fff) > + return 1; > + } else { > + if (REG_IN_RANGE(reg, 0x1000, 0x1fff) || > + REG_IN_RANGE(reg, 0x9000, 0x9fff) || > + REG_IN_RANGE(reg, 0xb000, 0xffff) || > + REG_IN_RANGE(reg, 0x15000, 0x21fff) || > + REG_IN_RANGE(reg, 0x23000, 0x23fff) || > + REG_IN_RANGE(reg, 0x25000, 0x2ffff) || > + REG_IN_RANGE(reg, 0x40000, 0xfffff) || > + REG_IN_RANGE(reg, 0x108000, 0x13ffff)) > + return 1; > + > + if (reg > 0x17ffff) > + return 1; > + } I think this would be more maintainable as a set of tables with the ranges. And a r/w flag could be stored in the table for the individual read/write blacklists. > + > + return 0; > +} > + > + > +#define REG_IN_READ_BLACKLIST(reg) 0 > +#define REG_IN_WRITE_BLACKLIST(reg) 0 > + > +static inline int > +reg_read_allowed(struct drm_device *dev, u32 offset) > +{ > + if (offset & 0x3) > + return 0; > + > + if (reg_in_rsvd_range(dev, offset)) > + return 0; > + > + if (REG_IN_READ_BLACKLIST(offset)) > + return 0; > + > + return 1; > +} > + > +static inline int > +reg_write_allowed(struct drm_device *dev, u32 offset) > +{ > + if (offset & 0x3) > + return 0; > + > + if (reg_in_rsvd_range(dev, offset)) > + return 0; > + > + if (REG_IN_WRITE_BLACKLIST(offset)) > + return 0; > + > + return 1; > +} > + > +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; > + int ret; > + > + if (!reg_read_allowed(dev, args->offset)) { > + args->value = 0xffffffff; > + return -EINVAL; > + } > + > + ret = i915_mutex_lock_interruptible(dev); I don't think you want i915_mutex_lock_interuptible() here just an ordinary mutex_lock_interruptible() - we don't need or desire to check if the GPU is wedged. > + if (ret) > + DRM_DEBUG_DRIVER("Couldn't acquire mutex, reading anyway\n"); > + > + args->value = (u32)i915_gt_read(dev_priv, args->offset); > + > + mutex_unlock(&dev->struct_mutex); Don't even think about unlocking an unlocked mutex. We either apply strict locking or we don't bother. I think we can assume that in the scenario where the mutex is blocked (from experience that is only from an OOPS within i915.ko) we can play with the registers to our hearts contents directly from userspace. > + > + 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; > + int ret; > + > + if (!reg_write_allowed(dev, args->offset)) > + return -EINVAL; > + > + ret = i915_mutex_lock_interruptible(dev); > + if (ret) > + DRM_DEBUG_DRIVER("Couldn't acquire mutex, writing anyway\n"); > + > + DRM_INFO("User space write %x %x\n", args->offset, (u32)args->value); > + dev_priv->user_tainted = true; Nice touch. Let's try add_taint(TAINT_USER) instead. > + > + i915_gt_write(dev_priv, args->offset, (u32)args->value); > + > + mutex_unlock(&dev->struct_mutex); > + > + return 0; > +} > + > /** > * Tells the intel_ips driver that the i915 driver is now loaded, if > * IPS got loaded first. > @@ -2276,6 +2403,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..4c66734 100644 > --- a/drivers/gpu/drm/i915/i915_drv.h > +++ b/drivers/gpu/drm/i915/i915_drv.h > @@ -703,6 +703,8 @@ typedef struct drm_i915_private { > struct intel_fbdev *fbdev; > > struct drm_property *broadcast_rgb_property; > + > + bool user_tainted; > } drm_i915_private_t; > > struct drm_i915_gem_object { > diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h > index c4d6dbf..68eeaa3 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,27 @@ 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; > +}; Haha! I was going to ask for this to handle arbitrary register sizes. What I think is also necessary is to be able to read/write a block of registers in a single call - thinking of some of the more complex procedures we may want to try from userspace (the pcode messages for example) or for snapshotting a set of registers. My dream would be a small domain specific language that would allow me to write a set of registers and poll for a result and then return that result. In the meantime I'd settle for passing an array of registers to be written and read in a single ioctl. > + > +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; > + > +}; > + > #endif /* _I915_DRM_H_ */ > -- > 1.7.3.4 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/intel-gfx -- Chris Wilson, Intel Open Source Technology Centre ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] drm/i915: read/write IOCTLs 2011-04-01 6:36 ` [PATCH] drm/i915: read/write IOCTLs Chris Wilson @ 2011-04-01 7:06 ` Ben Widawsky 2011-04-01 7:32 ` Chris Wilson 0 siblings, 1 reply; 19+ messages in thread From: Ben Widawsky @ 2011-04-01 7:06 UTC (permalink / raw) To: Chris Wilson; +Cc: intel-gfx On Fri, Apr 01, 2011 at 07:36:56AM +0100, Chris Wilson wrote: > Nice. The consensus is that this ioctl is required, just a few comments > inline. Nobody likes it, but it could pay off, especially if we end up with weird non-mappable registers we wish to allow user space to read. I had actually decided to add another field (something like a bus) to the interface just in case. > > +static inline int > > +reg_in_rsvd_range(struct drm_device *dev, u32 reg) > > +{ > > + if (INTEL_INFO(dev)->gen < 6) { > > + if (REG_IN_RANGE(reg, 0x1000, 0x1fff) || > > + ((IS_BROADWATER(dev) || IS_CRESTLINE(dev)) ? > > + REG_IN_RANGE(reg, 0x4000, 0x4fff) : 0) || > > + REG_IN_RANGE(reg, 0x9000, 0x9fff) || > > + REG_IN_RANGE(reg, 0xb000, 0xffff) || > > + REG_IN_RANGE(reg, 0x14000, 0x2ffff) || > > + REG_IN_RANGE(reg, 0x40000, 0x5ffff)) > > + return 1; > > + > > + if (reg > 0x73fff) > > + return 1; > > + } else { > > + if (REG_IN_RANGE(reg, 0x1000, 0x1fff) || > > + REG_IN_RANGE(reg, 0x9000, 0x9fff) || > > + REG_IN_RANGE(reg, 0xb000, 0xffff) || > > + REG_IN_RANGE(reg, 0x15000, 0x21fff) || > > + REG_IN_RANGE(reg, 0x23000, 0x23fff) || > > + REG_IN_RANGE(reg, 0x25000, 0x2ffff) || > > + REG_IN_RANGE(reg, 0x40000, 0xfffff) || > > + REG_IN_RANGE(reg, 0x108000, 0x13ffff)) > > + return 1; > > + > > + if (reg > 0x17ffff) > > + return 1; > > + } > > I think this would be more maintainable as a set of tables with the > ranges. And a r/w flag could be stored in the table for the individual > read/write blacklists. That's fine with me. When I started it was much less complicated than by the time I submitted the patch. > > + if (ret) > > + DRM_DEBUG_DRIVER("Couldn't acquire mutex, reading anyway\n"); > > + > > + args->value = (u32)i915_gt_read(dev_priv, args->offset); > > + > > + mutex_unlock(&dev->struct_mutex); > > Don't even think about unlocking an unlocked mutex. We either apply > strict locking or we don't bother. I think we can assume that in the > scenario where the mutex is blocked (from experience that is only from an > OOPS within i915.ko) we can play with the registers to our hearts contents > directly from userspace. This was a bug in my patch... you mean? ret = mutex_lock_interruptible(); if (ret) reg_read if (!ret) mutex_unlock() > > +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; > > + int ret; > > + > > + if (!reg_write_allowed(dev, args->offset)) > > + return -EINVAL; > > + > > + ret = i915_mutex_lock_interruptible(dev); > > + if (ret) > > + DRM_DEBUG_DRIVER("Couldn't acquire mutex, writing anyway\n"); > > + > > + DRM_INFO("User space write %x %x\n", args->offset, (u32)args->value); > > + dev_priv->user_tainted = true; > > Nice touch. Let's try add_taint(TAINT_USER) instead. Cool! I didn't know about that interface. I'd like to be able to query this at runtime as opposed to an OOPS... I'm too tired to look if it supports that right now, but if it does, I'll switch. > > > > +struct drm_intel_read_reg { > > + /* register offset to read */ > > + __u32 offset; > > + > > + /* register size, RFU */ > > + __u8 size; > > + > > + /* return value, high 4 bytes RFU */ > > + __u64 value; > > +}; > > Haha! I was going to ask for this to handle arbitrary register sizes. > What I think is also necessary is to be able to read/write a block of > registers in a single call - thinking of some of the more complex > procedures we may want to try from userspace (the pcode messages for > example) or for snapshotting a set of registers. I brought this question up at our meeting on Thursday. The consensus to was just have the 1 register at a time because the overhead of the syscall was small enough to make it not matter. I think I prefer the single read/write a little bit just because it keeps the kernel code simpler, but I don't care enough to argue, and I most certainly don't care enough to set up some kind of performance test for it. Anyone else care enough to argue? > > My dream would be a small domain specific language that would allow me to > write a set of registers and poll for a result and then return that > result. In the meantime I'd settle for passing an array of registers to be > written and read in a single ioctl. > GFX User mode driver? :) > -- > Chris Wilson, Intel Open Source Technology Centre Thanks for the feedback. I'll try to fix it up when I wake up. Ben ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] drm/i915: read/write IOCTLs 2011-04-01 7:06 ` Ben Widawsky @ 2011-04-01 7:32 ` Chris Wilson 2011-04-01 18:51 ` Eric Anholt 0 siblings, 1 reply; 19+ messages in thread From: Chris Wilson @ 2011-04-01 7:32 UTC (permalink / raw) To: Ben Widawsky; +Cc: intel-gfx On Fri, 1 Apr 2011 00:06:37 -0700, Ben Widawsky <ben@bwidawsk.net> wrote: > On Fri, Apr 01, 2011 at 07:36:56AM +0100, Chris Wilson wrote: > > Nice. The consensus is that this ioctl is required, just a few comments > > inline. > > Nobody likes it, but it could pay off, especially if we end up with > weird non-mappable registers we wish to allow user space to read. I had > actually decided to add another field (something like a bus) to the > interface just in case. I think most of that can and should be done in userspace. Eventually we should have the list of registers and be able to use the spec names and have those translated into the correct offset for the chipset. Has anyone succeeded in getting a machine-readable description of the registers? Or am I just dreaming that we have real documentation somewhere? > This was a bug in my patch... you mean? > ret = mutex_lock_interruptible(); > if (ret) > reg_read > > if (!ret) > mutex_unlock() Slightly better in avoiding the kernel killer. ;-) I'm just not happy about haphazard locking. Can we do simple and safe locking and revisit it if a real use-case for brute-forcing the read/write is found? > > > +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; > > > + int ret; > > > + > > > + if (!reg_write_allowed(dev, args->offset)) > > > + return -EINVAL; > > > + > > > + ret = i915_mutex_lock_interruptible(dev); > > > + if (ret) > > > + DRM_DEBUG_DRIVER("Couldn't acquire mutex, writing anyway\n"); > > > + > > > + DRM_INFO("User space write %x %x\n", args->offset, (u32)args->value); > > > + dev_priv->user_tainted = true; > > > > Nice touch. Let's try add_taint(TAINT_USER) instead. > > Cool! I didn't know about that interface. I'd like to be able to query > this at runtime as opposed to an OOPS... I'm too tired to look if it > supports that right now, but if it does, I'll switch. There's an equivalent get_taint(), but we have no way of knowing if that was us or not. We do need to taint the oops (as having the hardware change behind our backs is evil personified). When were you thinking of using dev_priv->user_tainted? > > > +struct drm_intel_read_reg { > > > + /* register offset to read */ > > > + __u32 offset; > > > + > > > + /* register size, RFU */ > > > + __u8 size; > > > + > > > + /* return value, high 4 bytes RFU */ > > > + __u64 value; > > > +}; > > > > Haha! I was going to ask for this to handle arbitrary register sizes. > > What I think is also necessary is to be able to read/write a block of > > registers in a single call - thinking of some of the more complex > > procedures we may want to try from userspace (the pcode messages for > > example) or for snapshotting a set of registers. > > I brought this question up at our meeting on Thursday. The consensus to > was just have the 1 register at a time because the overhead of the > syscall was small enough to make it not matter. I think I prefer the > single read/write a little bit just because it keeps the kernel code > simpler, but I don't care enough to argue, and I most certainly don't > care enough to set up some kind of performance test for it. Anyone else > care enough to argue? It's not the performance I care about, but the atomicity. There seem to be a growing abundance of messaging systems within the chip driven by combinations of registers. I'd feel happier if we could send a message without fouling or being fouled by the kernel. -Chris -- Chris Wilson, Intel Open Source Technology Centre ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] drm/i915: read/write IOCTLs 2011-04-01 7:32 ` Chris Wilson @ 2011-04-01 18:51 ` Eric Anholt 2011-04-02 6:46 ` Chris Wilson 0 siblings, 1 reply; 19+ messages in thread From: Eric Anholt @ 2011-04-01 18:51 UTC (permalink / raw) To: Chris Wilson, Ben Widawsky; +Cc: intel-gfx [-- Attachment #1.1: Type: text/plain, Size: 3263 bytes --] On Fri, 01 Apr 2011 08:32:09 +0100, Chris Wilson <chris@chris-wilson.co.uk> wrote: > On Fri, 1 Apr 2011 00:06:37 -0700, Ben Widawsky <ben@bwidawsk.net> wrote: > > On Fri, Apr 01, 2011 at 07:36:56AM +0100, Chris Wilson wrote: > > > Nice. The consensus is that this ioctl is required, just a few comments > > > inline. > > > > Nobody likes it, but it could pay off, especially if we end up with > > weird non-mappable registers we wish to allow user space to read. I had > > actually decided to add another field (something like a bus) to the > > interface just in case. > > I think most of that can and should be done in userspace. Eventually we > should have the list of registers and be able to use the spec names and > have those translated into the correct offset for the chipset. > > Has anyone succeeded in getting a machine-readable description of the > registers? Or am I just dreaming that we have real documentation > somewhere? > > > This was a bug in my patch... you mean? > > ret = mutex_lock_interruptible(); > > if (ret) > > reg_read > > > > if (!ret) > > mutex_unlock() > > Slightly better in avoiding the kernel killer. ;-) > > I'm just not happy about haphazard locking. Can we do simple and safe > locking and revisit it if a real use-case for brute-forcing the read/write > is found? The concern we had was "I want to be sure that when the GPU is hung we can still reg_dump so people can write bug reports. The mutex lock try should have a timeout, and we should go ahead and just try forcewaking and reading the reg anyway after a while." > > > > +struct drm_intel_read_reg { > > > > + /* register offset to read */ > > > > + __u32 offset; > > > > + > > > > + /* register size, RFU */ > > > > + __u8 size; > > > > + > > > > + /* return value, high 4 bytes RFU */ > > > > + __u64 value; > > > > +}; > > > > > > Haha! I was going to ask for this to handle arbitrary register sizes. > > > What I think is also necessary is to be able to read/write a block of > > > registers in a single call - thinking of some of the more complex > > > procedures we may want to try from userspace (the pcode messages for > > > example) or for snapshotting a set of registers. > > > > I brought this question up at our meeting on Thursday. The consensus to > > was just have the 1 register at a time because the overhead of the > > syscall was small enough to make it not matter. I think I prefer the > > single read/write a little bit just because it keeps the kernel code > > simpler, but I don't care enough to argue, and I most certainly don't > > care enough to set up some kind of performance test for it. Anyone else > > care enough to argue? > > It's not the performance I care about, but the atomicity. There seem to be > a growing abundance of messaging systems within the chip driven by > combinations of registers. I'd feel happier if we could send a message > without fouling or being fouled by the kernel. Generally for those things, you end up needing to poll in the middle. Let's not build a more complicated interface than required for current use-cases in userland (reading many registers, or writing an arbitrary one), without solving a specific problem. [-- Attachment #1.2: Type: application/pgp-signature, Size: 197 bytes --] [-- Attachment #2: Type: text/plain, Size: 159 bytes --] _______________________________________________ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/intel-gfx ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] drm/i915: read/write IOCTLs 2011-04-01 18:51 ` Eric Anholt @ 2011-04-02 6:46 ` Chris Wilson 2011-04-02 15:16 ` Ben Widawsky 2011-04-04 1:35 ` Ben Widawsky 0 siblings, 2 replies; 19+ messages in thread From: Chris Wilson @ 2011-04-02 6:46 UTC (permalink / raw) To: Eric Anholt, Ben Widawsky; +Cc: intel-gfx On Fri, 01 Apr 2011 11:51:23 -0700, Eric Anholt <eric@anholt.net> wrote: > On Fri, 01 Apr 2011 08:32:09 +0100, Chris Wilson <chris@chris-wilson.co.uk> wrote: > > I'm just not happy about haphazard locking. Can we do simple and safe > > locking and revisit it if a real use-case for brute-forcing the read/write > > is found? > > The concern we had was "I want to be sure that when the GPU is hung we > can still reg_dump so people can write bug reports. The mutex lock try > should have a timeout, and we should go ahead and just try forcewaking > and reading the reg anyway after a while." If the GPU has hung (and more importantly in such a way as to block the mutex - nowadays that is it an OOPS with the lock held), we can simply do the reg read/write from the userspace without worry. But for bug reports, I'd much rather we automatically capture any register that we might need than rely on the frustrated user doing the right thing. So I don't see having the haphazard locking in the kernel, for everyone to criticise us for, justified. > > It's not the performance I care about, but the atomicity. There seem to be > > a growing abundance of messaging systems within the chip driven by > > combinations of registers. I'd feel happier if we could send a message > > without fouling or being fouled by the kernel. > > Generally for those things, you end up needing to poll in the middle. > Let's not build a more complicated interface than required for current > use-cases in userland (reading many registers, or writing an arbitrary > one), without solving a specific problem. What I guess I was trying to express was that we need to be very clear what the interface is for and the limitations about its use. For the more complicated set of registers, we can and should expose knobs in the debugfs to read and write them. For instance, to control the render clock frequencies and thresholds. But perhaps we do need to reconsider the performance aspect. intel_gpu_top samples the ring HEAD and TAIL at around 10KHz and forcing gt-wake is about 50 microseconds... I hope I'm mistaken, because even batched that is doomed. Ben, do you mind checking that thought experiment with a little hard fact? -Chris -- Chris Wilson, Intel Open Source Technology Centre ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] drm/i915: read/write IOCTLs 2011-04-02 6:46 ` Chris Wilson @ 2011-04-02 15:16 ` Ben Widawsky 2011-04-04 1:35 ` Ben Widawsky 1 sibling, 0 replies; 19+ messages in thread From: Ben Widawsky @ 2011-04-02 15:16 UTC (permalink / raw) To: Chris Wilson; +Cc: intel-gfx On Sat, Apr 02, 2011 at 07:46:31AM +0100, Chris Wilson wrote: > > But perhaps we do need to reconsider the performance aspect. intel_gpu_top > samples the ring HEAD and TAIL at around 10KHz and forcing gt-wake is > about 50 microseconds... I hope I'm mistaken, because even batched that is > doomed. Ben, do you mind checking that thought experiment with a little > hard fact? I can get some numbers for it... but I'd like to further the discussion a bit since I have to go to the office to get a SNB, and typing is easier than doing that right now :). I too think we might be doomed. At the very least we have the POSTING_READ, which is expensive. The udelays may or may not actually occur (I'll find out). Let's not forget too that we do fix other tools, not just intel_gpu_top, and those tools don't poll, and don't care about timing (granted they're mostly less interesting too). I think what we really need to try to defer the forcewake_put, as well as something like the last patch I sent <1301105269-23970-2-git-send-email-ben@bwidawsk.net> to remove the need to protect force_wake_get with struct_mutex, and keep a refcount. I'd rather get the current stuff accepted, port the tools, and then make improvements to the performance. However if you feel the order must be another way, I can work with that. > -Chris > Thanks. Ben ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] drm/i915: read/write IOCTLs 2011-04-02 6:46 ` Chris Wilson 2011-04-02 15:16 ` Ben Widawsky @ 2011-04-04 1:35 ` Ben Widawsky 2011-04-04 7:36 ` Chris Wilson 1 sibling, 1 reply; 19+ messages in thread From: Ben Widawsky @ 2011-04-04 1:35 UTC (permalink / raw) To: Chris Wilson; +Cc: intel-gfx On Sat, Apr 02, 2011 at 07:46:31AM +0100, Chris Wilson wrote: > What I guess I was trying to express was that we need to be very clear > what the interface is for and the limitations about its use. > > For the more complicated set of registers, we can and should expose knobs > in the debugfs to read and write them. For instance, to control the render > clock frequencies and thresholds. > > But perhaps we do need to reconsider the performance aspect. intel_gpu_top > samples the ring HEAD and TAIL at around 10KHz and forcing gt-wake is > about 50 microseconds... I hope I'm mistaken, because even batched that is > doomed. Ben, do you mind checking that thought experiment with a little > hard fact? Here is the data from ~100 samples while playing playing Armacycles Advanced measured off of d-i-f 7f58aabc369014fda3a4a33604ba0a1b63b941ac. min 02.775us max 19.402us avg 07.057us stddev 02.819us When I do a cat /sys/kernel/debug/dri/0/i915_gem_interrupt, I always get 3 reads, in a similar pattern to this: 6) ! 285.852 us | __gen6_gt_force_wake_get(); 6) 1.944 us | __gen6_gt_force_wake_get(); 6) 1.854 us | __gen6_gt_force_wake_get(); Not sure why that case is so different. > -Chris Ben ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH] drm/i915: read/write IOCTLs 2011-04-04 1:35 ` Ben Widawsky @ 2011-04-04 7:36 ` Chris Wilson 0 siblings, 0 replies; 19+ messages in thread From: Chris Wilson @ 2011-04-04 7:36 UTC (permalink / raw) To: Ben Widawsky; +Cc: intel-gfx On Sun, 3 Apr 2011 18:35:04 -0700, Ben Widawsky <ben@bwidawsk.net> wrote: > Here is the data from ~100 samples while playing playing Armacycles Advanced > measured off of d-i-f 7f58aabc369014fda3a4a33604ba0a1b63b941ac. > > min 02.775us > max 19.402us > avg 07.057us > stddev 02.819us > > When I do a cat /sys/kernel/debug/dri/0/i915_gem_interrupt, I always get > 3 reads, in a similar pattern to this: > > 6) ! 285.852 us | __gen6_gt_force_wake_get(); > 6) 1.944 us | __gen6_gt_force_wake_get(); > 6) 1.854 us | __gen6_gt_force_wake_get(); > > Not sure why that case is so different. Presumably the high cost is when we need to wait for the GT to power up and the hardware has its own hysteresis and will delay before powering down again. [It also looks like we always have to wait at least for one loop. Perhaps a posting read is in order?] So at least we don't have to worry about doing that ourselves - adding a spinlock just for performance optimisation on a seldom used debugging ioctl is painful. -Chris -- Chris Wilson, Intel Open Source Technology Centre ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2011-04-08 17:29 UTC | newest] Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2011-04-01 1:31 [PATCH] drm/i915: read/write IOCTLs Ben Widawsky 2011-04-01 1:39 ` [PATCH v2] " Ben Widawsky 2011-04-01 23:54 ` Read/Write IOCTL compromise Ben Widawsky 2011-04-06 21:38 ` No more read/write ioctls Ben Widawsky 2011-04-08 17:10 ` Eric Anholt 2011-04-08 17:29 ` Chris Wilson 2011-04-01 23:54 ` [PATCH v3 1/2] drm/i915: read/write ioctls for userspace Ben Widawsky 2011-04-04 17:14 ` Chris Wilson 2011-04-05 1:30 ` Read/Write ioctls Ben Widawsky 2011-04-05 1:30 ` [PATCH v4] drm/i915: read/write ioctls for userspace Ben Widawsky 2011-04-01 23:54 ` [PATCH v3 2/2] drm/i915: debugfs for register write taint Ben Widawsky 2011-04-01 6:36 ` [PATCH] drm/i915: read/write IOCTLs Chris Wilson 2011-04-01 7:06 ` Ben Widawsky 2011-04-01 7:32 ` Chris Wilson 2011-04-01 18:51 ` Eric Anholt 2011-04-02 6:46 ` Chris Wilson 2011-04-02 15:16 ` Ben Widawsky 2011-04-04 1:35 ` Ben Widawsky 2011-04-04 7:36 ` Chris Wilson
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).