CHROMIUM: drm/evdi: add 32-bit ioctl compatibility routines. Module drm/evdi defines several ioctl structs containing user pointers. These require special handling for 32-bit ioctl compatibility. BUG=chrome-os-partner:59063 TEST=connect elm to DL3 docking station with HDMI TV Change-Id: I8dca1f1ea851bc11a4b24192d5959a7aab03e4b8 Signed-off-by: Haixia Shi <hshi@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/406013 Reviewed-by: Daniel Kurtz <djkurtz@chromium.org> (cherry picked from commit a84fb131cefe054ba942f21cd068791d4e736569) Reviewed-on: https://chromium-review.googlesource.com/407123 Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
diff --git a/drivers/gpu/drm/evdi/Makefile b/drivers/gpu/drm/evdi/Makefile index 411cab0..d1a7044 100644 --- a/drivers/gpu/drm/evdi/Makefile +++ b/drivers/gpu/drm/evdi/Makefile
@@ -9,5 +9,7 @@ evdi-y := evdi_drv.o evdi_modeset.o evdi_connector.o evdi_encoder.o evdi_main.o evdi_fb.o evdi_gem.o evdi_stats.o evdi_painter.o evdi_debug.o evdi_cursor.o +evdi-$(CONFIG_COMPAT) += evdi_ioc32.o + obj-$(CONFIG_DRM_EVDI) := evdi.o
diff --git a/drivers/gpu/drm/evdi/evdi_drv.c b/drivers/gpu/drm/evdi/evdi_drv.c index 88b553a..c0bb8e9 100644 --- a/drivers/gpu/drm/evdi/evdi_drv.c +++ b/drivers/gpu/drm/evdi/evdi_drv.c
@@ -55,7 +55,7 @@ .unlocked_ioctl = drm_ioctl, .release = drm_release, #ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, + .compat_ioctl = evdi_compat_ioctl, #endif .llseek = noop_llseek, };
diff --git a/drivers/gpu/drm/evdi/evdi_drv.h b/drivers/gpu/drm/evdi/evdi_drv.h index 547a902..607aeb6 100644 --- a/drivers/gpu/drm/evdi/evdi_drv.h +++ b/drivers/gpu/drm/evdi/evdi_drv.h
@@ -77,6 +77,10 @@ int evdi_driver_unload(struct drm_device *dev); void evdi_driver_preclose(struct drm_device *dev, struct drm_file *file_priv); +#ifdef CONFIG_COMPAT +long evdi_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#endif + int evdi_fbdev_init(struct drm_device *dev); void evdi_fbdev_cleanup(struct drm_device *dev); void evdi_fbdev_unplug(struct drm_device *dev);
diff --git a/drivers/gpu/drm/evdi/evdi_ioc32.c b/drivers/gpu/drm/evdi/evdi_ioc32.c new file mode 100644 index 0000000..1c6a4c4 --- /dev/null +++ b/drivers/gpu/drm/evdi/evdi_ioc32.c
@@ -0,0 +1,125 @@ +/** + * evdi_ioc32.c + * + * Copyright (c) 2016 The Chromium OS Authors + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/compat.h> + +#include <drm/drmP.h> +#include <drm/drm_edid.h> +#include <drm/evdi_drm.h> + +#include "evdi_drv.h" + +struct drm_evdi_connect32 { + int32_t connected; + int32_t dev_index; + uint32_t edid_ptr32; + uint32_t edid_length; +}; + +struct drm_evdi_grabpix32 { + uint32_t mode; + int32_t buf_width; + int32_t buf_height; + int32_t buf_byte_stride; + uint32_t buffer_ptr32; + int32_t num_rects; + uint32_t rects_ptr32; +}; + +static int compat_evdi_connect(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct drm_evdi_connect32 req32; + struct drm_evdi_connect __user *request; + + if (copy_from_user(&req32, (void __user *)arg, sizeof(req32))) + return -EFAULT; + + request = compat_alloc_user_space(sizeof(*request)); + if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) + || __put_user(req32.connected, &request->connected) + || __put_user(req32.dev_index, &request->dev_index) + || __put_user((void __user *)(unsigned long)req32.edid_ptr32, + &request->edid) + || __put_user(req32.edid_length, &request->edid_length)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_EVDI_CONNECT, + (unsigned long)request); +} + +static int compat_evdi_grabpix(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct drm_evdi_grabpix32 req32; + struct drm_evdi_grabpix __user *request; + + if (copy_from_user(&req32, (void __user *)arg, sizeof(req32))) + return -EFAULT; + + request = compat_alloc_user_space(sizeof(*request)); + if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) + || __put_user(req32.mode, &request->mode) + || __put_user(req32.buf_width, &request->buf_width) + || __put_user(req32.buf_height, &request->buf_height) + || __put_user(req32.buf_byte_stride, &request->buf_byte_stride) + || __put_user((void __user *)(unsigned long)req32.buffer_ptr32, + &request->buffer) + || __put_user(req32.num_rects, &request->num_rects) + || __put_user((void __user *)(unsigned long)req32.rects_ptr32, + &request->rects)) + return -EFAULT; + + return drm_ioctl(file, DRM_IOCTL_EVDI_GRABPIX, + (unsigned long)request); +} + +static drm_ioctl_compat_t *evdi_compat_ioctls[] = { + [DRM_EVDI_CONNECT] = compat_evdi_connect, + [DRM_EVDI_GRABPIX] = compat_evdi_grabpix, +}; + +/** + * Called whenever a 32-bit process running under a 64-bit kernel + * performs an ioctl on /dev/dri/card<n>. + * + * \param filp file pointer. + * \param cmd command. + * \param arg user argument. + * \return zero on success or negative number on failure. + */ +long evdi_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + unsigned int nr = DRM_IOCTL_NR(cmd); + drm_ioctl_compat_t *fn = NULL; + int ret; + + if (nr < DRM_COMMAND_BASE || nr >= DRM_COMMAND_END) + return drm_compat_ioctl(filp, cmd, arg); + + if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(evdi_compat_ioctls)) + fn = evdi_compat_ioctls[nr - DRM_COMMAND_BASE]; + + if (fn != NULL) + ret = (*fn) (filp, cmd, arg); + else + ret = drm_ioctl(filp, cmd, arg); + + return ret; +}