/* Copyright (c) 2014,2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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. * */ #include #include #include #include #include "../ion_priv.h" #include "../compat_ion.h" struct compat_ion_flush_data { compat_ion_user_handle_t handle; compat_int_t fd; compat_uptr_t vaddr; compat_uint_t offset; compat_uint_t length; }; struct compat_ion_prefetch_regions { compat_uint_t vmid; compat_uptr_t sizes; compat_uint_t nr_sizes; }; struct compat_ion_prefetch_data { compat_int_t heap_id; compat_ulong_t len; compat_uptr_t regions; compat_uint_t nr_regions; }; #define COMPAT_ION_IOC_CLEAN_CACHES _IOWR(ION_IOC_MSM_MAGIC, 0, \ struct compat_ion_flush_data) #define COMPAT_ION_IOC_INV_CACHES _IOWR(ION_IOC_MSM_MAGIC, 1, \ struct compat_ion_flush_data) #define COMPAT_ION_IOC_CLEAN_INV_CACHES _IOWR(ION_IOC_MSM_MAGIC, 2, \ struct compat_ion_flush_data) #define COMPAT_ION_IOC_PREFETCH _IOWR(ION_IOC_MSM_MAGIC, 3, \ struct compat_ion_prefetch_data) #define COMPAT_ION_IOC_DRAIN _IOWR(ION_IOC_MSM_MAGIC, 4, \ struct compat_ion_prefetch_data) static int compat_get_ion_flush_data( struct compat_ion_flush_data __user *data32, struct ion_flush_data __user *data) { compat_ion_user_handle_t h; compat_int_t i; compat_uptr_t u; compat_ulong_t l; int err; err = get_user(h, &data32->handle); err |= put_user(h, &data->handle); err |= get_user(i, &data32->fd); err |= put_user(i, &data->fd); err |= get_user(u, &data32->vaddr); /* upper bits won't get set, zero them */ err |= put_user(NULL, &data->vaddr); err |= put_user(u, (compat_uptr_t *)&data->vaddr); err |= get_user(l, &data32->offset); err |= put_user(l, &data->offset); err |= get_user(l, &data32->length); err |= put_user(l, &data->length); return err; } static int compat_get_ion_prefetch_data( struct compat_ion_prefetch_data __user *data32, struct ion_prefetch_data __user *data, size_t stack_offset) { compat_int_t i; compat_ulong_t l; compat_uint_t u; int err, j, k; compat_uint_t nr_regions, nr_sizes; struct compat_ion_prefetch_regions __user *regions32; struct ion_prefetch_regions __user *regions; compat_uptr_t ptr; err = get_user(i, &data32->heap_id); err |= put_user(i, &data->heap_id); err |= get_user(l, &data32->len); err |= put_user(l, &data->len); err |= get_user(nr_regions, &data32->nr_regions); err |= put_user(nr_regions, &data->nr_regions); err |= get_user(ptr, &data32->regions); regions32 = compat_ptr(ptr); if (err) return err; stack_offset += nr_regions * sizeof(*regions); regions = compat_alloc_user_space(stack_offset); if (!regions) return -EFAULT; err |= put_user(regions, &data->regions); for (k = 0; k < nr_regions; k++) { compat_size_t __user *sizes32; size_t __user *sizes; err |= get_user(u, ®ions32[k].vmid); err |= put_user(u, ®ions[k].vmid); err |= get_user(nr_sizes, ®ions32[k].nr_sizes); err |= put_user(nr_sizes, ®ions[k].nr_sizes); err |= get_user(ptr, ®ions32[k].sizes); sizes32 = compat_ptr(ptr); if (err) return -EFAULT; stack_offset += nr_sizes * sizeof(*sizes); sizes = compat_alloc_user_space(stack_offset); if (!sizes) return -EFAULT; err |= put_user(sizes, ®ions[k].sizes); for (j = 0; j < nr_sizes; j++) { compat_size_t s; err |= get_user(s, &sizes32[j]); err |= put_user(s, &sizes[j]); } } return err; } static unsigned int convert_cmd(unsigned int cmd) { switch (cmd) { case COMPAT_ION_IOC_CLEAN_CACHES: return ION_IOC_CLEAN_CACHES; case COMPAT_ION_IOC_INV_CACHES: return ION_IOC_INV_CACHES; case COMPAT_ION_IOC_CLEAN_INV_CACHES: return ION_IOC_CLEAN_INV_CACHES; case COMPAT_ION_IOC_PREFETCH: return ION_IOC_PREFETCH; case COMPAT_ION_IOC_DRAIN: return ION_IOC_DRAIN; default: return cmd; } } long compat_msm_ion_ioctl(struct ion_client *client, unsigned int cmd, unsigned long arg) { switch (cmd) { case COMPAT_ION_IOC_CLEAN_CACHES: case COMPAT_ION_IOC_INV_CACHES: case COMPAT_ION_IOC_CLEAN_INV_CACHES: { struct compat_ion_flush_data __user *data32; struct ion_flush_data __user *data; int err; data32 = compat_ptr(arg); data = compat_alloc_user_space(sizeof(*data)); if (data == NULL) return -EFAULT; err = compat_get_ion_flush_data(data32, data); if (err) return err; return msm_ion_custom_ioctl(client, convert_cmd(cmd), (unsigned long)data); } case COMPAT_ION_IOC_PREFETCH: case COMPAT_ION_IOC_DRAIN: { struct compat_ion_prefetch_data __user *data32; struct ion_prefetch_data __user *data; int err; data32 = compat_ptr(arg); data = compat_alloc_user_space(sizeof(*data)); if (data == NULL) return -EFAULT; err = compat_get_ion_prefetch_data(data32, data, sizeof(*data)); if (err) return err; return msm_ion_custom_ioctl(client, convert_cmd(cmd), (unsigned long)data); } default: if (is_compat_task()) return -ENOIOCTLCMD; else return msm_ion_custom_ioctl(client, cmd, arg); } }