// SPDX-License-Identifier: GPL-2.0
|
/*
|
* Copyright (C) 2011 Google, Inc.
|
*/
|
|
#include <linux/kernel.h>
|
#include <linux/file.h>
|
#include <linux/fs.h>
|
#include <linux/uaccess.h>
|
|
#include "ion.h"
|
|
union ion_ioctl_arg {
|
struct ion_allocation_data allocation;
|
struct ion_heap_query query;
|
struct ion_phys_data phys;
|
};
|
|
static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg)
|
{
|
switch (cmd) {
|
case ION_IOC_HEAP_QUERY:
|
if (arg->query.reserved0 ||
|
arg->query.reserved1 ||
|
arg->query.reserved2)
|
return -EINVAL;
|
break;
|
default:
|
break;
|
}
|
|
return 0;
|
}
|
|
/* fix up the cases where the ioctl direction bits are incorrect */
|
static unsigned int ion_ioctl_dir(unsigned int cmd)
|
{
|
switch (cmd) {
|
default:
|
return _IOC_DIR(cmd);
|
}
|
}
|
|
long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
{
|
int ret = 0;
|
unsigned int dir;
|
union ion_ioctl_arg data;
|
|
dir = ion_ioctl_dir(cmd);
|
|
if (_IOC_SIZE(cmd) > sizeof(data))
|
return -EINVAL;
|
|
/*
|
* The copy_from_user is unconditional here for both read and write
|
* to do the validate. If there is no write for the ioctl, the
|
* buffer is cleared
|
*/
|
if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
|
return -EFAULT;
|
|
ret = validate_ioctl_arg(cmd, &data);
|
if (ret) {
|
pr_warn_once("%s: ioctl validate failed\n", __func__);
|
return ret;
|
}
|
|
if (!(dir & _IOC_WRITE))
|
memset(&data, 0, sizeof(data));
|
|
switch (cmd) {
|
case ION_IOC_ALLOC:
|
{
|
int fd;
|
|
fd = ion_alloc(data.allocation.len,
|
data.allocation.heap_id_mask,
|
data.allocation.flags);
|
if (fd < 0)
|
return fd;
|
|
data.allocation.fd = fd;
|
|
break;
|
}
|
case ION_IOC_HEAP_QUERY:
|
ret = ion_query_heaps(&data.query);
|
break;
|
case ION_IOC_GET_PHYS:
|
ret = ion_get_phys(&data.phys);
|
break;
|
default:
|
return -ENOTTY;
|
}
|
|
if (dir & _IOC_READ) {
|
if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd)))
|
return -EFAULT;
|
}
|
return ret;
|
}
|