| // SPDX-License-Identifier: GPL-2.0 |
| // Copyright (C) 2013 - 2020 Intel Corporation |
| |
| #include <linux/compat.h> |
| #include <linux/errno.h> |
| #include <linux/uaccess.h> |
| |
| #include <uapi/linux/ipu-psys.h> |
| |
| #include "ipu-psys.h" |
| |
| static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| { |
| long ret = -ENOTTY; |
| |
| if (file->f_op->unlocked_ioctl) |
| ret = file->f_op->unlocked_ioctl(file, cmd, arg); |
| |
| return ret; |
| } |
| |
| struct ipu_psys_buffer32 { |
| u64 len; |
| union { |
| int fd; |
| compat_uptr_t userptr; |
| u64 reserved; |
| } base; |
| u32 data_offset; |
| u32 bytes_used; |
| u32 flags; |
| u32 reserved[2]; |
| } __packed; |
| |
| struct ipu_psys_command32 { |
| u64 issue_id; |
| u64 user_token; |
| u32 priority; |
| compat_uptr_t pg_manifest; |
| compat_uptr_t buffers; |
| int pg; |
| u32 pg_manifest_size; |
| u32 bufcount; |
| u32 min_psys_freq; |
| u32 frame_counter; |
| u32 reserved[2]; |
| } __packed; |
| |
| struct ipu_psys_manifest32 { |
| u32 index; |
| u32 size; |
| compat_uptr_t manifest; |
| u32 reserved[5]; |
| } __packed; |
| |
| static int |
| get_ipu_psys_command32(struct ipu_psys_command *kp, |
| struct ipu_psys_command32 __user *up) |
| { |
| compat_uptr_t pgm, bufs; |
| bool access_ok; |
| |
| access_ok = access_ok(up, sizeof(struct ipu_psys_command32)); |
| if (!access_ok || get_user(kp->issue_id, &up->issue_id) || |
| get_user(kp->user_token, &up->user_token) || |
| get_user(kp->priority, &up->priority) || |
| get_user(pgm, &up->pg_manifest) || |
| get_user(bufs, &up->buffers) || |
| get_user(kp->pg, &up->pg) || |
| get_user(kp->pg_manifest_size, &up->pg_manifest_size) || |
| get_user(kp->bufcount, &up->bufcount) || |
| get_user(kp->min_psys_freq, &up->min_psys_freq) || |
| get_user(kp->frame_counter, &up->frame_counter) |
| ) |
| return -EFAULT; |
| |
| kp->pg_manifest = compat_ptr(pgm); |
| kp->buffers = compat_ptr(bufs); |
| |
| return 0; |
| } |
| |
| static int |
| get_ipu_psys_buffer32(struct ipu_psys_buffer *kp, |
| struct ipu_psys_buffer32 __user *up) |
| { |
| compat_uptr_t ptr; |
| bool access_ok; |
| |
| access_ok = access_ok(up, sizeof(struct ipu_psys_buffer32)); |
| if (!access_ok || get_user(kp->len, &up->len) || |
| get_user(ptr, &up->base.userptr) || |
| get_user(kp->data_offset, &up->data_offset) || |
| get_user(kp->bytes_used, &up->bytes_used) || |
| get_user(kp->flags, &up->flags)) |
| return -EFAULT; |
| |
| kp->base.userptr = compat_ptr(ptr); |
| |
| return 0; |
| } |
| |
| static int |
| put_ipu_psys_buffer32(struct ipu_psys_buffer *kp, |
| struct ipu_psys_buffer32 __user *up) |
| { |
| bool access_ok; |
| |
| access_ok = access_ok(up, sizeof(struct ipu_psys_buffer32)); |
| if (!access_ok || put_user(kp->len, &up->len) || |
| put_user(kp->base.fd, &up->base.fd) || |
| put_user(kp->data_offset, &up->data_offset) || |
| put_user(kp->bytes_used, &up->bytes_used) || |
| put_user(kp->flags, &up->flags)) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| static int |
| get_ipu_psys_manifest32(struct ipu_psys_manifest *kp, |
| struct ipu_psys_manifest32 __user *up) |
| { |
| compat_uptr_t ptr; |
| bool access_ok; |
| |
| access_ok = access_ok(up, sizeof(struct ipu_psys_manifest32)); |
| if (!access_ok || get_user(kp->index, &up->index) || |
| get_user(kp->size, &up->size) || get_user(ptr, &up->manifest)) |
| return -EFAULT; |
| |
| kp->manifest = compat_ptr(ptr); |
| |
| return 0; |
| } |
| |
| static int |
| put_ipu_psys_manifest32(struct ipu_psys_manifest *kp, |
| struct ipu_psys_manifest32 __user *up) |
| { |
| compat_uptr_t ptr = (u32)((unsigned long)kp->manifest); |
| bool access_ok; |
| |
| access_ok = access_ok(up, sizeof(struct ipu_psys_manifest32)); |
| if (!access_ok || put_user(kp->index, &up->index) || |
| put_user(kp->size, &up->size) || put_user(ptr, &up->manifest)) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| #define IPU_IOC_GETBUF32 _IOWR('A', 4, struct ipu_psys_buffer32) |
| #define IPU_IOC_PUTBUF32 _IOWR('A', 5, struct ipu_psys_buffer32) |
| #define IPU_IOC_QCMD32 _IOWR('A', 6, struct ipu_psys_command32) |
| #define IPU_IOC_CMD_CANCEL32 _IOWR('A', 8, struct ipu_psys_command32) |
| #define IPU_IOC_GET_MANIFEST32 _IOWR('A', 9, struct ipu_psys_manifest32) |
| |
| long ipu_psys_compat_ioctl32(struct file *file, unsigned int cmd, |
| unsigned long arg) |
| { |
| union { |
| struct ipu_psys_buffer buf; |
| struct ipu_psys_command cmd; |
| struct ipu_psys_event ev; |
| struct ipu_psys_manifest m; |
| } karg; |
| int compatible_arg = 1; |
| int err = 0; |
| void __user *up = compat_ptr(arg); |
| |
| switch (cmd) { |
| case IPU_IOC_GETBUF32: |
| cmd = IPU_IOC_GETBUF; |
| break; |
| case IPU_IOC_PUTBUF32: |
| cmd = IPU_IOC_PUTBUF; |
| break; |
| case IPU_IOC_QCMD32: |
| cmd = IPU_IOC_QCMD; |
| break; |
| case IPU_IOC_GET_MANIFEST32: |
| cmd = IPU_IOC_GET_MANIFEST; |
| break; |
| } |
| |
| switch (cmd) { |
| case IPU_IOC_GETBUF: |
| case IPU_IOC_PUTBUF: |
| err = get_ipu_psys_buffer32(&karg.buf, up); |
| compatible_arg = 0; |
| break; |
| case IPU_IOC_QCMD: |
| err = get_ipu_psys_command32(&karg.cmd, up); |
| compatible_arg = 0; |
| break; |
| case IPU_IOC_GET_MANIFEST: |
| err = get_ipu_psys_manifest32(&karg.m, up); |
| compatible_arg = 0; |
| break; |
| } |
| if (err) |
| return err; |
| |
| if (compatible_arg) { |
| err = native_ioctl(file, cmd, (unsigned long)up); |
| } else { |
| mm_segment_t old_fs = get_fs(); |
| |
| set_fs(KERNEL_DS); |
| err = native_ioctl(file, cmd, (unsigned long)&karg); |
| set_fs(old_fs); |
| } |
| |
| if (err) |
| return err; |
| |
| switch (cmd) { |
| case IPU_IOC_GETBUF: |
| err = put_ipu_psys_buffer32(&karg.buf, up); |
| break; |
| case IPU_IOC_GET_MANIFEST: |
| err = put_ipu_psys_manifest32(&karg.m, up); |
| break; |
| } |
| return err; |
| } |