329 lines
8.6 KiB
C
329 lines
8.6 KiB
C
/*
|
|
* Copyright (c) 2014-2015, 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 <linux/compat.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/msm_ion.h>
|
|
|
|
#include "adsprpc_compat.h"
|
|
#include "adsprpc_shared.h"
|
|
|
|
#define COMPAT_FASTRPC_IOCTL_INVOKE \
|
|
_IOWR('R', 1, struct compat_fastrpc_ioctl_invoke)
|
|
#define COMPAT_FASTRPC_IOCTL_MMAP \
|
|
_IOWR('R', 2, struct compat_fastrpc_ioctl_mmap)
|
|
#define COMPAT_FASTRPC_IOCTL_MUNMAP \
|
|
_IOWR('R', 3, struct compat_fastrpc_ioctl_munmap)
|
|
#define COMPAT_FASTRPC_IOCTL_INVOKE_FD \
|
|
_IOWR('R', 4, struct compat_fastrpc_ioctl_invoke_fd)
|
|
#define COMPAT_FASTRPC_IOCTL_INIT \
|
|
_IOWR('R', 6, struct compat_fastrpc_ioctl_init)
|
|
|
|
struct compat_remote_buf {
|
|
compat_uptr_t pv; /* buffer pointer */
|
|
compat_ssize_t len; /* length of buffer */
|
|
};
|
|
|
|
union compat_remote_arg {
|
|
struct compat_remote_buf buf;
|
|
compat_uint_t h;
|
|
};
|
|
|
|
struct compat_fastrpc_ioctl_invoke {
|
|
compat_uint_t handle; /* remote handle */
|
|
compat_uint_t sc; /* scalars describing the data */
|
|
compat_uptr_t pra; /* remote arguments list */
|
|
};
|
|
|
|
struct compat_fastrpc_ioctl_invoke_fd {
|
|
struct compat_fastrpc_ioctl_invoke inv;
|
|
compat_uptr_t fds; /* fd list */
|
|
};
|
|
|
|
struct compat_fastrpc_ioctl_mmap {
|
|
compat_int_t fd; /* ion fd */
|
|
compat_uint_t flags; /* flags for dsp to map with */
|
|
compat_uptr_t vaddrin; /* optional virtual address */
|
|
compat_ssize_t size; /* size */
|
|
compat_uptr_t vaddrout; /* dsps virtual address */
|
|
};
|
|
|
|
struct compat_fastrpc_ioctl_munmap {
|
|
compat_uptr_t vaddrout; /* address to unmap */
|
|
compat_ssize_t size; /* size */
|
|
};
|
|
|
|
struct compat_fastrpc_ioctl_init {
|
|
compat_uint_t flags; /* one of FASTRPC_INIT_* macros */
|
|
compat_uptr_t file; /* pointer to elf file */
|
|
compat_int_t filelen; /* elf file length */
|
|
compat_int_t filefd; /* ION fd for the file */
|
|
compat_uptr_t mem; /* mem for the PD */
|
|
compat_int_t memlen; /* mem length */
|
|
compat_int_t memfd; /* ION fd for the mem */
|
|
};
|
|
|
|
static int compat_get_fastrpc_ioctl_invoke(
|
|
struct compat_fastrpc_ioctl_invoke_fd __user *inv32,
|
|
struct fastrpc_ioctl_invoke_fd __user **inva,
|
|
unsigned int cmd)
|
|
{
|
|
compat_uint_t u, sc;
|
|
compat_ssize_t s;
|
|
compat_uptr_t p;
|
|
struct fastrpc_ioctl_invoke_fd *inv;
|
|
union compat_remote_arg *pra32;
|
|
union remote_arg *pra;
|
|
int err, len, num, j;
|
|
|
|
err = get_user(sc, &inv32->inv.sc);
|
|
if (err)
|
|
return err;
|
|
|
|
len = REMOTE_SCALARS_LENGTH(sc);
|
|
VERIFY(err, NULL != (inv = compat_alloc_user_space(
|
|
sizeof(*inv) + len * sizeof(*pra))));
|
|
if (err)
|
|
return -EFAULT;
|
|
|
|
pra = (union remote_arg *)(inv + 1);
|
|
err = put_user(pra, &inv->inv.pra);
|
|
err |= put_user(sc, &inv->inv.sc);
|
|
err |= get_user(u, &inv32->inv.handle);
|
|
err |= put_user(u, &inv->inv.handle);
|
|
err |= get_user(p, &inv32->inv.pra);
|
|
if (err)
|
|
return err;
|
|
|
|
pra32 = compat_ptr(p);
|
|
pra = (union remote_arg *)(inv + 1);
|
|
num = REMOTE_SCALARS_INBUFS(sc) + REMOTE_SCALARS_OUTBUFS(sc);
|
|
for (j = 0; j < num; j++) {
|
|
err |= get_user(p, &pra32[j].buf.pv);
|
|
err |= put_user(p, (uintptr_t *)&pra[j].buf.pv);
|
|
err |= get_user(s, &pra32[j].buf.len);
|
|
err |= put_user(s, &pra[j].buf.len);
|
|
}
|
|
for (j = 0; j < REMOTE_SCALARS_INHANDLES(sc); j++) {
|
|
err |= get_user(u, &pra32[num + j].h);
|
|
err |= put_user(u, &pra[num + j].h);
|
|
}
|
|
|
|
err |= put_user(NULL, &inv->fds);
|
|
if (cmd == COMPAT_FASTRPC_IOCTL_INVOKE_FD) {
|
|
err |= get_user(p, &inv32->fds);
|
|
err |= put_user(p, (compat_uptr_t *)&inv->fds);
|
|
}
|
|
|
|
*inva = inv;
|
|
return err;
|
|
}
|
|
|
|
static int compat_put_fastrpc_ioctl_invoke(
|
|
struct compat_fastrpc_ioctl_invoke_fd __user *inv32,
|
|
struct fastrpc_ioctl_invoke_fd __user *inv)
|
|
{
|
|
compat_uptr_t p;
|
|
compat_uint_t u, h;
|
|
union compat_remote_arg *pra32;
|
|
union remote_arg *pra;
|
|
int err, i, num;
|
|
|
|
err = get_user(u, &inv32->inv.sc);
|
|
err |= get_user(p, &inv32->inv.pra);
|
|
if (err)
|
|
return err;
|
|
|
|
pra32 = compat_ptr(p);
|
|
pra = (union remote_arg *)(inv + 1);
|
|
num = REMOTE_SCALARS_INBUFS(u) + REMOTE_SCALARS_OUTBUFS(u)
|
|
+ REMOTE_SCALARS_INHANDLES(u);
|
|
for (i = 0; i < REMOTE_SCALARS_OUTHANDLES(u); i++) {
|
|
err |= get_user(h, &pra[num + i].h);
|
|
err |= put_user(h, &pra32[num + i].h);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int compat_get_fastrpc_ioctl_mmap(
|
|
struct compat_fastrpc_ioctl_mmap __user *map32,
|
|
struct fastrpc_ioctl_mmap __user *map)
|
|
{
|
|
compat_uint_t u;
|
|
compat_int_t i;
|
|
compat_ssize_t s;
|
|
compat_uptr_t p;
|
|
int err;
|
|
|
|
err = get_user(i, &map32->fd);
|
|
err |= put_user(i, &map->fd);
|
|
err |= get_user(u, &map32->flags);
|
|
err |= put_user(u, &map->flags);
|
|
err |= get_user(p, &map32->vaddrin);
|
|
err |= put_user(p, (uintptr_t *)&map->vaddrin);
|
|
err |= get_user(s, &map32->size);
|
|
err |= put_user(s, &map->size);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int compat_put_fastrpc_ioctl_mmap(
|
|
struct compat_fastrpc_ioctl_mmap __user *map32,
|
|
struct fastrpc_ioctl_mmap __user *map)
|
|
{
|
|
compat_uptr_t p;
|
|
int err;
|
|
|
|
err = get_user(p, &map->vaddrout);
|
|
err |= put_user(p, &map32->vaddrout);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int compat_get_fastrpc_ioctl_munmap(
|
|
struct compat_fastrpc_ioctl_munmap __user *unmap32,
|
|
struct fastrpc_ioctl_munmap __user *unmap)
|
|
{
|
|
compat_uptr_t p;
|
|
compat_ssize_t s;
|
|
int err;
|
|
|
|
err = get_user(p, &unmap32->vaddrout);
|
|
err |= put_user(p, &unmap->vaddrout);
|
|
err |= get_user(s, &unmap32->size);
|
|
err |= put_user(s, &unmap->size);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int compat_get_fastrpc_ioctl_init(
|
|
struct compat_fastrpc_ioctl_init __user *init32,
|
|
struct fastrpc_ioctl_init __user *init)
|
|
{
|
|
compat_uint_t u;
|
|
compat_uptr_t p;
|
|
compat_int_t i;
|
|
int err;
|
|
|
|
err = get_user(u, &init32->flags);
|
|
err |= put_user(u, &init->flags);
|
|
err |= get_user(p, &init32->file);
|
|
err |= put_user(p, &init->file);
|
|
err |= get_user(i, &init32->filelen);
|
|
err |= put_user(i, &init->filelen);
|
|
err |= get_user(i, &init32->filefd);
|
|
err |= put_user(i, &init->filefd);
|
|
err |= get_user(p, &init32->mem);
|
|
err |= put_user(p, &init->mem);
|
|
err |= get_user(i, &init32->memlen);
|
|
err |= put_user(i, &init->memlen);
|
|
err |= get_user(i, &init32->memfd);
|
|
err |= put_user(i, &init->memfd);
|
|
|
|
return err;
|
|
}
|
|
|
|
long compat_fastrpc_device_ioctl(struct file *filp, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
int err = 0;
|
|
|
|
if (!filp->f_op || !filp->f_op->unlocked_ioctl)
|
|
return -ENOTTY;
|
|
|
|
switch (cmd) {
|
|
case COMPAT_FASTRPC_IOCTL_INVOKE:
|
|
case COMPAT_FASTRPC_IOCTL_INVOKE_FD:
|
|
{
|
|
struct compat_fastrpc_ioctl_invoke_fd __user *inv32;
|
|
struct fastrpc_ioctl_invoke_fd __user *inv;
|
|
long ret;
|
|
|
|
inv32 = compat_ptr(arg);
|
|
VERIFY(err, 0 == compat_get_fastrpc_ioctl_invoke(inv32,
|
|
&inv, cmd));
|
|
if (err)
|
|
return err;
|
|
ret = filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_INVOKE_FD,
|
|
(unsigned long)inv);
|
|
if (ret)
|
|
return ret;
|
|
VERIFY(err, 0 == compat_put_fastrpc_ioctl_invoke(inv32, inv));
|
|
return err;
|
|
}
|
|
case COMPAT_FASTRPC_IOCTL_MMAP:
|
|
{
|
|
struct compat_fastrpc_ioctl_mmap __user *map32;
|
|
struct fastrpc_ioctl_mmap __user *map;
|
|
long ret;
|
|
|
|
map32 = compat_ptr(arg);
|
|
VERIFY(err, NULL != (map = compat_alloc_user_space(
|
|
sizeof(*map))));
|
|
if (err)
|
|
return -EFAULT;
|
|
VERIFY(err, 0 == compat_get_fastrpc_ioctl_mmap(map32, map));
|
|
if (err)
|
|
return err;
|
|
ret = filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MMAP,
|
|
(unsigned long)map);
|
|
if (ret)
|
|
return ret;
|
|
VERIFY(err, 0 == compat_put_fastrpc_ioctl_mmap(map32, map));
|
|
return err;
|
|
}
|
|
case COMPAT_FASTRPC_IOCTL_MUNMAP:
|
|
{
|
|
struct compat_fastrpc_ioctl_munmap __user *unmap32;
|
|
struct fastrpc_ioctl_munmap __user *unmap;
|
|
|
|
unmap32 = compat_ptr(arg);
|
|
VERIFY(err, NULL != (unmap = compat_alloc_user_space(
|
|
sizeof(*unmap))));
|
|
if (err)
|
|
return -EFAULT;
|
|
VERIFY(err, 0 == compat_get_fastrpc_ioctl_munmap(unmap32,
|
|
unmap));
|
|
if (err)
|
|
return err;
|
|
return filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_MUNMAP,
|
|
(unsigned long)unmap);
|
|
}
|
|
case COMPAT_FASTRPC_IOCTL_INIT:
|
|
{
|
|
struct compat_fastrpc_ioctl_init __user *init32;
|
|
struct fastrpc_ioctl_init __user *init;
|
|
|
|
init32 = compat_ptr(arg);
|
|
VERIFY(err, NULL != (init = compat_alloc_user_space(
|
|
sizeof(*init))));
|
|
if (err)
|
|
return -EFAULT;
|
|
VERIFY(err, 0 == compat_get_fastrpc_ioctl_init(init32,
|
|
init));
|
|
if (err)
|
|
return err;
|
|
return filp->f_op->unlocked_ioctl(filp, FASTRPC_IOCTL_INIT,
|
|
(unsigned long)init);
|
|
}
|
|
case FASTRPC_IOCTL_SETMODE:
|
|
return filp->f_op->unlocked_ioctl(filp, cmd,
|
|
(unsigned long)compat_ptr(arg));
|
|
default:
|
|
return -ENOIOCTLCMD;
|
|
}
|
|
}
|