diff mbox series

[v6,48/49] linux-user: Split out ioctl

Message ID 20190118213122.22865-48-richard.henderson@linaro.org
State Superseded
Headers show
Series linux-user: Split do_syscall | expand

Commit Message

Richard Henderson Jan. 18, 2019, 9:31 p.m. UTC
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

---
 linux-user/syscall-defs.h      |   1 +
 linux-user/syscall-ioctl.inc.c | 873 +++++++++++++++++++++++++++++++++
 linux-user/syscall.c           | 843 +------------------------------
 linux-user/strace.list         |   3 -
 4 files changed, 875 insertions(+), 845 deletions(-)
 create mode 100644 linux-user/syscall-ioctl.inc.c

-- 
2.17.2

Comments

Laurent Vivier Feb. 13, 2019, 1:09 p.m. UTC | #1
Hi,

this one is really a cut'n'paste but it introduces a problem with 
qemu-alpha, I don't know how/why:

  $ sudo unshare --ipc --uts --pid --fork --kill-child --mount chroot chroot/alpha/sid/
  Unsupported ioctl: cmd=0x80047476

And then it hangs.

I'll try to debug.

Thanks,
Laurent

On 18/01/2019 22:31, Richard Henderson wrote:
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

> ---

>  linux-user/syscall-defs.h      |   1 +

>  linux-user/syscall-ioctl.inc.c | 873 +++++++++++++++++++++++++++++++++

>  linux-user/syscall.c           | 843 +------------------------------

>  linux-user/strace.list         |   3 -

>  4 files changed, 875 insertions(+), 845 deletions(-)

>  create mode 100644 linux-user/syscall-ioctl.inc.c

> 

> diff --git a/linux-user/syscall-defs.h b/linux-user/syscall-defs.h

> index f8f280f376..f58b9745a4 100644

> --- a/linux-user/syscall-defs.h

> +++ b/linux-user/syscall-defs.h

> @@ -61,6 +61,7 @@ SYSCALL_DEF(getppid);

>  #ifdef TARGET_NR_getxpid

>  SYSCALL_DEF(getxpid);

>  #endif

> +SYSCALL_DEF(ioctl, ARG_DEC, ARG_HEX);

>  #ifdef TARGET_NR_ipc

>  SYSCALL_DEF_ARGS(ipc, ARG_HEX, ARG_DEC, ARG_DEC, ARG_HEX, ARG_PTR, ARG_HEX);

>  #endif

> diff --git a/linux-user/syscall-ioctl.inc.c b/linux-user/syscall-ioctl.inc.c

> new file mode 100644

> index 0000000000..820994105f

> --- /dev/null

> +++ b/linux-user/syscall-ioctl.inc.c

> @@ -0,0 +1,873 @@

> +/*

> + *  Linux ioctl syscall implementation

> + *  Copyright (c) 2003 Fabrice Bellard

> + *

> + *  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/>.

> + */

> +

> +typedef struct IOCTLEntry IOCTLEntry;

> +

> +typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp,

> +                             int fd, int cmd, abi_long arg);

> +

> +struct IOCTLEntry {

> +    int target_cmd;

> +    unsigned int host_cmd;

> +    const char *name;

> +    int access;

> +    do_ioctl_fn *do_ioctl;

> +    const argtype arg_type[5];

> +};

> +

> +#define IOC_R 0x0001

> +#define IOC_W 0x0002

> +#define IOC_RW (IOC_R | IOC_W)

> +

> +#define MAX_STRUCT_SIZE 4096

> +

> +#ifdef CONFIG_FIEMAP

> +/*

> + * So fiemap access checks don't overflow on 32 bit systems.

> + * This is very slightly smaller than the limit imposed by

> + * the underlying kernel.

> + */

> +#define FIEMAP_MAX_EXTENTS ((UINT_MAX - sizeof(struct fiemap))  \

> +                            / sizeof(struct fiemap_extent))

> +

> +static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp,

> +                                       int fd, int cmd, abi_long arg)

> +{

> +    /*

> +     * The parameter for this ioctl is a struct fiemap followed

> +     * by an array of struct fiemap_extent whose size is set

> +     * in fiemap->fm_extent_count. The array is filled in by the

> +     * ioctl.

> +     */

> +    int target_size_in, target_size_out;

> +    struct fiemap *fm;

> +    const argtype *arg_type = ie->arg_type;

> +    const argtype extent_arg_type[] = { MK_STRUCT(STRUCT_fiemap_extent) };

> +    void *argptr, *p;

> +    abi_long ret;

> +    int i, extent_size = thunk_type_size(extent_arg_type, 0);

> +    uint32_t outbufsz;

> +    int free_fm = 0;

> +

> +    assert(arg_type[0] == TYPE_PTR);

> +    assert(ie->access == IOC_RW);

> +    arg_type++;

> +    target_size_in = thunk_type_size(arg_type, 0);

> +    argptr = lock_user(VERIFY_READ, arg, target_size_in, 1);

> +    if (!argptr) {

> +        return -TARGET_EFAULT;

> +    }

> +    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> +    unlock_user(argptr, arg, 0);

> +    fm = (struct fiemap *)buf_temp;

> +    if (fm->fm_extent_count > FIEMAP_MAX_EXTENTS) {

> +        return -TARGET_EINVAL;

> +    }

> +

> +    outbufsz = sizeof(*fm) + sizeof(struct fiemap_extent) * fm->fm_extent_count;

> +

> +    if (outbufsz > MAX_STRUCT_SIZE) {

> +        /*

> +         * We can't fit all the extents into the fixed size buffer.

> +         * Allocate one that is large enough and use it instead.

> +         */

> +        fm = g_try_malloc(outbufsz);

> +        if (!fm) {

> +            return -TARGET_ENOMEM;

> +        }

> +        memcpy(fm, buf_temp, sizeof(struct fiemap));

> +        free_fm = 1;

> +    }

> +    ret = get_errno(safe_ioctl(fd, ie->host_cmd, fm));

> +    if (!is_error(ret)) {

> +        target_size_out = target_size_in;

> +        /*

> +         * An extent_count of 0 means we were only counting the extents

> +         * so there are no structs to copy

> +         */

> +        if (fm->fm_extent_count != 0) {

> +            target_size_out += fm->fm_mapped_extents * extent_size;

> +        }

> +        argptr = lock_user(VERIFY_WRITE, arg, target_size_out, 0);

> +        if (!argptr) {

> +            ret = -TARGET_EFAULT;

> +        } else {

> +            /* Convert the struct fiemap */

> +            thunk_convert(argptr, fm, arg_type, THUNK_TARGET);

> +            if (fm->fm_extent_count != 0) {

> +                p = argptr + target_size_in;

> +                /* ...and then all the struct fiemap_extents */

> +                for (i = 0; i < fm->fm_mapped_extents; i++) {

> +                    thunk_convert(p, &fm->fm_extents[i], extent_arg_type,

> +                                  THUNK_TARGET);

> +                    p += extent_size;

> +                }

> +            }

> +            unlock_user(argptr, arg, target_size_out);

> +        }

> +    }

> +    if (free_fm) {

> +        g_free(fm);

> +    }

> +    return ret;

> +}

> +#endif

> +

> +static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,

> +                                int fd, int cmd, abi_long arg)

> +{

> +    const argtype *arg_type = ie->arg_type;

> +    int target_size;

> +    void *argptr;

> +    int ret;

> +    struct ifconf *host_ifconf;

> +    uint32_t outbufsz;

> +    const argtype ifreq_arg_type[] = { MK_STRUCT(STRUCT_sockaddr_ifreq) };

> +    int target_ifreq_size;

> +    int nb_ifreq;

> +    int free_buf = 0;

> +    int i;

> +    int target_ifc_len;

> +    abi_long target_ifc_buf;

> +    int host_ifc_len;

> +    char *host_ifc_buf;

> +

> +    assert(arg_type[0] == TYPE_PTR);

> +    assert(ie->access == IOC_RW);

> +

> +    arg_type++;

> +    target_size = thunk_type_size(arg_type, 0);

> +

> +    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> +    if (!argptr) {

> +        return -TARGET_EFAULT;

> +    }

> +    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> +    unlock_user(argptr, arg, 0);

> +

> +    host_ifconf = (struct ifconf *)(unsigned long)buf_temp;

> +    target_ifc_len = host_ifconf->ifc_len;

> +    target_ifc_buf = (abi_long)(unsigned long)host_ifconf->ifc_buf;

> +

> +    target_ifreq_size = thunk_type_size(ifreq_arg_type, 0);

> +    nb_ifreq = target_ifc_len / target_ifreq_size;

> +    host_ifc_len = nb_ifreq * sizeof(struct ifreq);

> +

> +    outbufsz = sizeof(*host_ifconf) + host_ifc_len;

> +    if (outbufsz > MAX_STRUCT_SIZE) {

> +        /*

> +         * We can't fit all the extents into the fixed size buffer.

> +         * Allocate one that is large enough and use it instead.

> +         */

> +        host_ifconf = malloc(outbufsz);

> +        if (!host_ifconf) {

> +            return -TARGET_ENOMEM;

> +        }

> +        memcpy(host_ifconf, buf_temp, sizeof(*host_ifconf));

> +        free_buf = 1;

> +    }

> +    host_ifc_buf = (char *)host_ifconf + sizeof(*host_ifconf);

> +

> +    host_ifconf->ifc_len = host_ifc_len;

> +    host_ifconf->ifc_buf = host_ifc_buf;

> +

> +    ret = get_errno(safe_ioctl(fd, ie->host_cmd, host_ifconf));

> +    if (!is_error(ret)) {

> +        /* convert host ifc_len to target ifc_len */

> +

> +        nb_ifreq = host_ifconf->ifc_len / sizeof(struct ifreq);

> +        target_ifc_len = nb_ifreq * target_ifreq_size;

> +        host_ifconf->ifc_len = target_ifc_len;

> +

> +        /* restore target ifc_buf */

> +

> +        host_ifconf->ifc_buf = (char *)(unsigned long)target_ifc_buf;

> +

> +        /* copy struct ifconf to target user */

> +

> +        argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);

> +        if (!argptr) {

> +            return -TARGET_EFAULT;

> +        }

> +        thunk_convert(argptr, host_ifconf, arg_type, THUNK_TARGET);

> +        unlock_user(argptr, arg, target_size);

> +

> +        /* copy ifreq[] to target user */

> +

> +        argptr = lock_user(VERIFY_WRITE, target_ifc_buf, target_ifc_len, 0);

> +        for (i = 0; i < nb_ifreq ; i++) {

> +            thunk_convert(argptr + i * target_ifreq_size,

> +                          host_ifc_buf + i * sizeof(struct ifreq),

> +                          ifreq_arg_type, THUNK_TARGET);

> +        }

> +        unlock_user(argptr, target_ifc_buf, target_ifc_len);

> +    }

> +

> +    if (free_buf) {

> +        free(host_ifconf);

> +    }

> +

> +    return ret;

> +}

> +

> +#if defined(CONFIG_USBFS)

> +#if HOST_LONG_BITS > 64

> +#error USBDEVFS thunks do not support >64 bit hosts yet.

> +#endif

> +struct live_urb {

> +    uint64_t target_urb_adr;

> +    uint64_t target_buf_adr;

> +    char *target_buf_ptr;

> +    struct usbdevfs_urb host_urb;

> +};

> +

> +static GHashTable *usbdevfs_urb_hashtable(void)

> +{

> +    static GHashTable *urb_hashtable;

> +

> +    if (!urb_hashtable) {

> +        urb_hashtable = g_hash_table_new(g_int64_hash, g_int64_equal);

> +    }

> +    return urb_hashtable;

> +}

> +

> +static void urb_hashtable_insert(struct live_urb *urb)

> +{

> +    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();

> +    g_hash_table_insert(urb_hashtable, urb, urb);

> +}

> +

> +static struct live_urb *urb_hashtable_lookup(uint64_t target_urb_adr)

> +{

> +    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();

> +    return g_hash_table_lookup(urb_hashtable, &target_urb_adr);

> +}

> +

> +static void urb_hashtable_remove(struct live_urb *urb)

> +{

> +    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();

> +    g_hash_table_remove(urb_hashtable, urb);

> +}

> +

> +static abi_long

> +do_ioctl_usbdevfs_reapurb(const IOCTLEntry *ie, uint8_t *buf_temp,

> +                          int fd, int cmd, abi_long arg)

> +{

> +    const argtype usbfsurb_arg_type[] = { MK_STRUCT(STRUCT_usbdevfs_urb) };

> +    const argtype ptrvoid_arg_type[] = { TYPE_PTRVOID, 0, 0 };

> +    struct live_urb *lurb;

> +    void *argptr;

> +    uint64_t hurb;

> +    int target_size;

> +    uintptr_t target_urb_adr;

> +    abi_long ret;

> +

> +    target_size = thunk_type_size(usbfsurb_arg_type, THUNK_TARGET);

> +

> +    memset(buf_temp, 0, sizeof(uint64_t));

> +    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> +    if (is_error(ret)) {

> +        return ret;

> +    }

> +

> +    memcpy(&hurb, buf_temp, sizeof(uint64_t));

> +    lurb = (void *)((uintptr_t)hurb - offsetof(struct live_urb, host_urb));

> +    if (!lurb->target_urb_adr) {

> +        return -TARGET_EFAULT;

> +    }

> +    urb_hashtable_remove(lurb);

> +    unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr,

> +                lurb->host_urb.buffer_length);

> +    lurb->target_buf_ptr = NULL;

> +

> +    /* restore the guest buffer pointer */

> +    lurb->host_urb.buffer = (void *)(uintptr_t)lurb->target_buf_adr;

> +

> +    /* update the guest urb struct */

> +    argptr = lock_user(VERIFY_WRITE, lurb->target_urb_adr, target_size, 0);

> +    if (!argptr) {

> +        g_free(lurb);

> +        return -TARGET_EFAULT;

> +    }

> +    thunk_convert(argptr, &lurb->host_urb, usbfsurb_arg_type, THUNK_TARGET);

> +    unlock_user(argptr, lurb->target_urb_adr, target_size);

> +

> +    target_size = thunk_type_size(ptrvoid_arg_type, THUNK_TARGET);

> +    /* write back the urb handle */

> +    argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);

> +    if (!argptr) {

> +        g_free(lurb);

> +        return -TARGET_EFAULT;

> +    }

> +

> +    /* GHashTable uses 64-bit keys but thunk_convert expects uintptr_t */

> +    target_urb_adr = lurb->target_urb_adr;

> +    thunk_convert(argptr, &target_urb_adr, ptrvoid_arg_type, THUNK_TARGET);

> +    unlock_user(argptr, arg, target_size);

> +

> +    g_free(lurb);

> +    return ret;

> +}

> +

> +static abi_long

> +do_ioctl_usbdevfs_discardurb(const IOCTLEntry *ie,

> +                             uint8_t *buf_temp __attribute__((unused)),

> +                             int fd, int cmd, abi_long arg)

> +{

> +    struct live_urb *lurb;

> +

> +    /* map target address back to host URB with metadata. */

> +    lurb = urb_hashtable_lookup(arg);

> +    if (!lurb) {

> +        return -TARGET_EFAULT;

> +    }

> +    return get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));

> +}

> +

> +static abi_long

> +do_ioctl_usbdevfs_submiturb(const IOCTLEntry *ie, uint8_t *buf_temp,

> +                            int fd, int cmd, abi_long arg)

> +{

> +    const argtype *arg_type = ie->arg_type;

> +    int target_size;

> +    abi_long ret;

> +    void *argptr;

> +    int rw_dir;

> +    struct live_urb *lurb;

> +

> +    /*

> +     * each submitted URB needs to map to a unique ID for the

> +     * kernel, and that unique ID needs to be a pointer to

> +     * host memory.  hence, we need to malloc for each URB.

> +     * isochronous transfers have a variable length struct.

> +     */

> +    arg_type++;

> +    target_size = thunk_type_size(arg_type, THUNK_TARGET);

> +

> +    /* construct host copy of urb and metadata */

> +    lurb = g_try_malloc0(sizeof(struct live_urb));

> +    if (!lurb) {

> +        return -TARGET_ENOMEM;

> +    }

> +

> +    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> +    if (!argptr) {

> +        g_free(lurb);

> +        return -TARGET_EFAULT;

> +    }

> +    thunk_convert(&lurb->host_urb, argptr, arg_type, THUNK_HOST);

> +    unlock_user(argptr, arg, 0);

> +

> +    lurb->target_urb_adr = arg;

> +    lurb->target_buf_adr = (uintptr_t)lurb->host_urb.buffer;

> +

> +    /* buffer space used depends on endpoint type so lock the entire buffer */

> +    /* control type urbs should check the buffer contents for true direction */

> +    rw_dir = lurb->host_urb.endpoint & USB_DIR_IN ? VERIFY_WRITE : VERIFY_READ;

> +    lurb->target_buf_ptr = lock_user(rw_dir, lurb->target_buf_adr,

> +                                     lurb->host_urb.buffer_length, 1);

> +    if (lurb->target_buf_ptr == NULL) {

> +        g_free(lurb);

> +        return -TARGET_EFAULT;

> +    }

> +

> +    /* update buffer pointer in host copy */

> +    lurb->host_urb.buffer = lurb->target_buf_ptr;

> +

> +    ret = get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));

> +    if (is_error(ret)) {

> +        unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr, 0);

> +        g_free(lurb);

> +    } else {

> +        urb_hashtable_insert(lurb);

> +    }

> +

> +    return ret;

> +}

> +#endif /* CONFIG_USBFS */

> +

> +static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,

> +                            int cmd, abi_long arg)

> +{

> +    void *argptr;

> +    struct dm_ioctl *host_dm;

> +    abi_long guest_data;

> +    uint32_t guest_data_size;

> +    int target_size;

> +    const argtype *arg_type = ie->arg_type;

> +    abi_long ret;

> +    void *big_buf = NULL;

> +    char *host_data;

> +

> +    arg_type++;

> +    target_size = thunk_type_size(arg_type, 0);

> +    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> +    if (!argptr) {

> +        ret = -TARGET_EFAULT;

> +        goto out;

> +    }

> +    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> +    unlock_user(argptr, arg, 0);

> +

> +    /* buf_temp is too small, so fetch things into a bigger buffer */

> +    big_buf = g_malloc0(((struct dm_ioctl *)buf_temp)->data_size * 2);

> +    memcpy(big_buf, buf_temp, target_size);

> +    buf_temp = big_buf;

> +    host_dm = big_buf;

> +

> +    guest_data = arg + host_dm->data_start;

> +    if ((guest_data - arg) < 0) {

> +        ret = -TARGET_EINVAL;

> +        goto out;

> +    }

> +    guest_data_size = host_dm->data_size - host_dm->data_start;

> +    host_data = (char *)host_dm + host_dm->data_start;

> +

> +    argptr = lock_user(VERIFY_READ, guest_data, guest_data_size, 1);

> +    if (!argptr) {

> +        ret = -TARGET_EFAULT;

> +        goto out;

> +    }

> +

> +    switch (ie->host_cmd) {

> +    case DM_REMOVE_ALL:

> +    case DM_LIST_DEVICES:

> +    case DM_DEV_CREATE:

> +    case DM_DEV_REMOVE:

> +    case DM_DEV_SUSPEND:

> +    case DM_DEV_STATUS:

> +    case DM_DEV_WAIT:

> +    case DM_TABLE_STATUS:

> +    case DM_TABLE_CLEAR:

> +    case DM_TABLE_DEPS:

> +    case DM_LIST_VERSIONS:

> +        /* no input data */

> +        break;

> +    case DM_DEV_RENAME:

> +    case DM_DEV_SET_GEOMETRY:

> +        /* data contains only strings */

> +        memcpy(host_data, argptr, guest_data_size);

> +        break;

> +    case DM_TARGET_MSG:

> +        memcpy(host_data, argptr, guest_data_size);

> +        *(uint64_t *)host_data = tswap64(*(uint64_t *)argptr);

> +        break;

> +    case DM_TABLE_LOAD:

> +    {

> +        void *gspec = argptr;

> +        void *cur_data = host_data;

> +        const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };

> +        int spec_size = thunk_type_size(arg_type, 0);

> +        int i;

> +

> +        for (i = 0; i < host_dm->target_count; i++) {

> +            struct dm_target_spec *spec = cur_data;

> +            uint32_t next;

> +            int slen;

> +

> +            thunk_convert(spec, gspec, arg_type, THUNK_HOST);

> +            slen = strlen((char *)gspec + spec_size) + 1;

> +            next = spec->next;

> +            spec->next = sizeof(*spec) + slen;

> +            strcpy((char *)&spec[1], gspec + spec_size);

> +            gspec += next;

> +            cur_data += spec->next;

> +        }

> +        break;

> +    }

> +    default:

> +        ret = -TARGET_EINVAL;

> +        unlock_user(argptr, guest_data, 0);

> +        goto out;

> +    }

> +    unlock_user(argptr, guest_data, 0);

> +

> +    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> +    if (!is_error(ret)) {

> +        guest_data = arg + host_dm->data_start;

> +        guest_data_size = host_dm->data_size - host_dm->data_start;

> +        argptr = lock_user(VERIFY_WRITE, guest_data, guest_data_size, 0);

> +        switch (ie->host_cmd) {

> +        case DM_REMOVE_ALL:

> +        case DM_DEV_CREATE:

> +        case DM_DEV_REMOVE:

> +        case DM_DEV_RENAME:

> +        case DM_DEV_SUSPEND:

> +        case DM_DEV_STATUS:

> +        case DM_TABLE_LOAD:

> +        case DM_TABLE_CLEAR:

> +        case DM_TARGET_MSG:

> +        case DM_DEV_SET_GEOMETRY:

> +            /* no return data */

> +            break;

> +        case DM_LIST_DEVICES:

> +        {

> +            struct dm_name_list *nl = (void *)host_dm + host_dm->data_start;

> +            uint32_t remaining_data = guest_data_size;

> +            void *cur_data = argptr;

> +            const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) };

> +            int nl_size = 12; /* can't use thunk_size due to alignment */

> +

> +            while (1) {

> +                uint32_t next = nl->next;

> +                if (next) {

> +                    nl->next = nl_size + (strlen(nl->name) + 1);

> +                }

> +                if (remaining_data < nl->next) {

> +                    host_dm->flags |= DM_BUFFER_FULL_FLAG;

> +                    break;

> +                }

> +                thunk_convert(cur_data, nl, arg_type, THUNK_TARGET);

> +                strcpy(cur_data + nl_size, nl->name);

> +                cur_data += nl->next;

> +                remaining_data -= nl->next;

> +                if (!next) {

> +                    break;

> +                }

> +                nl = (void *)nl + next;

> +            }

> +            break;

> +        }

> +        case DM_DEV_WAIT:

> +        case DM_TABLE_STATUS:

> +        {

> +            struct dm_target_spec *spec

> +                = (void *)host_dm + host_dm->data_start;

> +            void *cur_data = argptr;

> +            const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };

> +            int spec_size = thunk_type_size(arg_type, 0);

> +            int i;

> +

> +            for (i = 0; i < host_dm->target_count; i++) {

> +                uint32_t next = spec->next;

> +                int slen = strlen((char *)&spec[1]) + 1;

> +                spec->next = (cur_data - argptr) + spec_size + slen;

> +                if (guest_data_size < spec->next) {

> +                    host_dm->flags |= DM_BUFFER_FULL_FLAG;

> +                    break;

> +                }

> +                thunk_convert(cur_data, spec, arg_type, THUNK_TARGET);

> +                strcpy(cur_data + spec_size, (char *)&spec[1]);

> +                cur_data = argptr + spec->next;

> +                spec = (void *)host_dm + host_dm->data_start + next;

> +            }

> +            break;

> +        }

> +        case DM_TABLE_DEPS:

> +        {

> +            void *hdata = (void *)host_dm + host_dm->data_start;

> +            int count = *(uint32_t *)hdata;

> +            uint64_t *hdev = hdata + 8;

> +            uint64_t *gdev = argptr + 8;

> +            int i;

> +

> +            *(uint32_t *)argptr = tswap32(count);

> +            for (i = 0; i < count; i++) {

> +                *gdev = tswap64(*hdev);

> +                gdev++;

> +                hdev++;

> +            }

> +            break;

> +        }

> +        case DM_LIST_VERSIONS:

> +        {

> +            struct dm_target_versions *vers

> +                = (void *)host_dm + host_dm->data_start;

> +            uint32_t remaining_data = guest_data_size;

> +            void *cur_data = argptr;

> +            const argtype arg_type[]

> +                = { MK_STRUCT(STRUCT_dm_target_versions) };

> +            int vers_size = thunk_type_size(arg_type, 0);

> +

> +            while (1) {

> +                uint32_t next = vers->next;

> +                if (next) {

> +                    vers->next = vers_size + strlen(vers->name) + 1;

> +                }

> +                if (remaining_data < vers->next) {

> +                    host_dm->flags |= DM_BUFFER_FULL_FLAG;

> +                    break;

> +                }

> +                thunk_convert(cur_data, vers, arg_type, THUNK_TARGET);

> +                strcpy(cur_data + vers_size, vers->name);

> +                cur_data += vers->next;

> +                remaining_data -= vers->next;

> +                if (!next) {

> +                    break;

> +                }

> +                vers = (void *)vers + next;

> +            }

> +            break;

> +        }

> +        default:

> +            unlock_user(argptr, guest_data, 0);

> +            ret = -TARGET_EINVAL;

> +            goto out;

> +        }

> +        unlock_user(argptr, guest_data, guest_data_size);

> +

> +        argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);

> +        if (!argptr) {

> +            ret = -TARGET_EFAULT;

> +            goto out;

> +        }

> +        thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);

> +        unlock_user(argptr, arg, target_size);

> +    }

> +out:

> +    g_free(big_buf);

> +    return ret;

> +}

> +

> +static abi_long do_ioctl_blkpg(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,

> +                               int cmd, abi_long arg)

> +{

> +    void *argptr;

> +    int target_size;

> +    const argtype *arg_type = ie->arg_type;

> +    const argtype part_arg_type[] = { MK_STRUCT(STRUCT_blkpg_partition) };

> +    abi_long ret;

> +    struct blkpg_ioctl_arg *host_blkpg = (void *)buf_temp;

> +    struct blkpg_partition host_part;

> +

> +    /* Read and convert blkpg */

> +    arg_type++;

> +    target_size = thunk_type_size(arg_type, 0);

> +    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> +    if (!argptr) {

> +        ret = -TARGET_EFAULT;

> +        goto out;

> +    }

> +    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> +    unlock_user(argptr, arg, 0);

> +

> +    switch (host_blkpg->op) {

> +    case BLKPG_ADD_PARTITION:

> +    case BLKPG_DEL_PARTITION:

> +        /* payload is struct blkpg_partition */

> +        break;

> +    default:

> +        /* Unknown opcode */

> +        ret = -TARGET_EINVAL;

> +        goto out;

> +    }

> +

> +    /* Read and convert blkpg->data */

> +    arg = (abi_long)(uintptr_t)host_blkpg->data;

> +    target_size = thunk_type_size(part_arg_type, 0);

> +    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> +    if (!argptr) {

> +        ret = -TARGET_EFAULT;

> +        goto out;

> +    }

> +    thunk_convert(&host_part, argptr, part_arg_type, THUNK_HOST);

> +    unlock_user(argptr, arg, 0);

> +

> +    /* Swizzle the data pointer to our local copy and call! */

> +    host_blkpg->data = &host_part;

> +    ret = get_errno(safe_ioctl(fd, ie->host_cmd, host_blkpg));

> +

> +out:

> +    return ret;

> +}

> +

> +static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp,

> +                            int fd, int cmd, abi_long arg)

> +{

> +    const argtype *arg_type = ie->arg_type;

> +    const StructEntry *se;

> +    const argtype *field_types;

> +    const int *dst_offsets, *src_offsets;

> +    int target_size;

> +    void *argptr;

> +    abi_ulong *target_rt_dev_ptr;

> +    unsigned long *host_rt_dev_ptr;

> +    abi_long ret;

> +    int i;

> +

> +    assert(ie->access == IOC_W);

> +    assert(*arg_type == TYPE_PTR);

> +    arg_type++;

> +    assert(*arg_type == TYPE_STRUCT);

> +    target_size = thunk_type_size(arg_type, 0);

> +    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> +    if (!argptr) {

> +        return -TARGET_EFAULT;

> +    }

> +    arg_type++;

> +    assert(*arg_type == (int)STRUCT_rtentry);

> +    se = struct_entries + *arg_type++;

> +    assert(se->convert[0] == NULL);

> +    /* convert struct here to be able to catch rt_dev string */

> +    field_types = se->field_types;

> +    dst_offsets = se->field_offsets[THUNK_HOST];

> +    src_offsets = se->field_offsets[THUNK_TARGET];

> +    for (i = 0; i < se->nb_fields; i++) {

> +        if (dst_offsets[i] == offsetof(struct rtentry, rt_dev)) {

> +            assert(*field_types == TYPE_PTRVOID);

> +            target_rt_dev_ptr = (abi_ulong *)(argptr + src_offsets[i]);

> +            host_rt_dev_ptr = (unsigned long *)(buf_temp + dst_offsets[i]);

> +            if (*target_rt_dev_ptr != 0) {

> +                *host_rt_dev_ptr = (unsigned long)lock_user_string(

> +                    tswapal(*target_rt_dev_ptr));

> +                if (!*host_rt_dev_ptr) {

> +                    unlock_user(argptr, arg, 0);

> +                    return -TARGET_EFAULT;

> +                }

> +            } else {

> +                *host_rt_dev_ptr = 0;

> +            }

> +            field_types++;

> +            continue;

> +        }

> +        field_types = thunk_convert(buf_temp + dst_offsets[i],

> +                                    argptr + src_offsets[i],

> +                                    field_types, THUNK_HOST);

> +    }

> +    unlock_user(argptr, arg, 0);

> +

> +    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> +    if (*host_rt_dev_ptr != 0) {

> +        unlock_user((void *)*host_rt_dev_ptr,

> +                    *target_rt_dev_ptr, 0);

> +    }

> +    return ret;

> +}

> +

> +static abi_long do_ioctl_kdsigaccept(const IOCTLEntry *ie, uint8_t *buf_temp,

> +                                     int fd, int cmd, abi_long arg)

> +{

> +    int sig = target_to_host_signal(arg);

> +    return get_errno(safe_ioctl(fd, ie->host_cmd, sig));

> +}

> +

> +#ifdef TIOCGPTPEER

> +static abi_long do_ioctl_tiocgptpeer(const IOCTLEntry *ie, uint8_t *buf_temp,

> +                                     int fd, int cmd, abi_long arg)

> +{

> +    int flags = target_to_host_bitmask(arg, fcntl_flags_tbl);

> +    return get_errno(safe_ioctl(fd, ie->host_cmd, flags));

> +}

> +#endif

> +

> +static IOCTLEntry ioctl_entries[] = {

> +#define IOCTL(cmd, access, ...)                                 \

> +    { TARGET_ ## cmd, cmd, #cmd, access, 0, {  __VA_ARGS__ } },

> +#define IOCTL_SPECIAL(cmd, access, dofn, ...)                           \

> +    { TARGET_ ## cmd, cmd, #cmd, access, dofn, {  __VA_ARGS__ } },

> +#define IOCTL_IGNORE(cmd)                       \

> +    { TARGET_ ## cmd, 0, #cmd },

> +#include "ioctls.h"

> +    { 0, 0, },

> +};

> +

> +/* ??? Implement proper locking for ioctls.  */

> +SYSCALL_IMPL(ioctl)

> +{

> +    int fd = arg1;

> +    abi_ulong cmd = arg2;

> +    abi_ulong arg = arg3;

> +    const IOCTLEntry *ie;

> +    const argtype *arg_type;

> +    abi_long ret;

> +    uint8_t buf_temp[MAX_STRUCT_SIZE];

> +    int target_size;

> +    void *argptr;

> +

> +    for (ie = ioctl_entries; ; ie++) {

> +        if (ie->target_cmd == 0) {

> +            gemu_log("Unsupported ioctl: cmd=0x%04lx\n", (long)cmd);

> +            return -TARGET_ENOSYS;

> +        }

> +        if (ie->target_cmd == cmd) {

> +            break;

> +        }

> +    }

> +

> +    arg_type = ie->arg_type;

> +    if (ie->do_ioctl) {

> +        return ie->do_ioctl(ie, buf_temp, fd, cmd, arg);

> +    } else if (!ie->host_cmd) {

> +        /*

> +         * Some architectures define BSD ioctls in their headers

> +         * that are not implemented in Linux.

> +         */

> +        return -TARGET_ENOSYS;

> +    }

> +

> +    switch (arg_type[0]) {

> +    case TYPE_NULL:

> +        /* no argument */

> +        ret = get_errno(safe_ioctl(fd, ie->host_cmd));

> +        break;

> +    case TYPE_PTRVOID:

> +    case TYPE_INT:

> +        ret = get_errno(safe_ioctl(fd, ie->host_cmd, arg));

> +        break;

> +    case TYPE_PTR:

> +        arg_type++;

> +        target_size = thunk_type_size(arg_type, 0);

> +        switch (ie->access) {

> +        case IOC_R:

> +            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> +            if (!is_error(ret)) {

> +                argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);

> +                if (!argptr) {

> +                    return -TARGET_EFAULT;

> +                }

> +                thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);

> +                unlock_user(argptr, arg, target_size);

> +            }

> +            break;

> +        case IOC_W:

> +            argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> +            if (!argptr) {

> +                return -TARGET_EFAULT;

> +            }

> +            thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> +            unlock_user(argptr, arg, 0);

> +            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> +            break;

> +        default:

> +        case IOC_RW:

> +            argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> +            if (!argptr) {

> +                return -TARGET_EFAULT;

> +            }

> +            thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> +            unlock_user(argptr, arg, 0);

> +            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> +            if (!is_error(ret)) {

> +                argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);

> +                if (!argptr) {

> +                    return -TARGET_EFAULT;

> +                }

> +                thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);

> +                unlock_user(argptr, arg, target_size);

> +            }

> +            break;

> +        }

> +        break;

> +    default:

> +        gemu_log("Unsupported ioctl type: cmd=0x%04lx type=%d\n",

> +                 (long)cmd, arg_type[0]);

> +        ret = -TARGET_ENOSYS;

> +        break;

> +    }

> +    return ret;

> +}

> diff --git a/linux-user/syscall.c b/linux-user/syscall.c

> index 82b7267b20..03ecf0a15a 100644

> --- a/linux-user/syscall.c

> +++ b/linux-user/syscall.c

> @@ -2935,846 +2935,6 @@ STRUCT_MAX

>  #undef STRUCT

>  #undef STRUCT_SPECIAL

>  

> -typedef struct IOCTLEntry IOCTLEntry;

> -

> -typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp,

> -                             int fd, int cmd, abi_long arg);

> -

> -struct IOCTLEntry {

> -    int target_cmd;

> -    unsigned int host_cmd;

> -    const char *name;

> -    int access;

> -    do_ioctl_fn *do_ioctl;

> -    const argtype arg_type[5];

> -};

> -

> -#define IOC_R 0x0001

> -#define IOC_W 0x0002

> -#define IOC_RW (IOC_R | IOC_W)

> -

> -#define MAX_STRUCT_SIZE 4096

> -

> -#ifdef CONFIG_FIEMAP

> -/* So fiemap access checks don't overflow on 32 bit systems.

> - * This is very slightly smaller than the limit imposed by

> - * the underlying kernel.

> - */

> -#define FIEMAP_MAX_EXTENTS ((UINT_MAX - sizeof(struct fiemap))  \

> -                            / sizeof(struct fiemap_extent))

> -

> -static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp,

> -                                       int fd, int cmd, abi_long arg)

> -{

> -    /* The parameter for this ioctl is a struct fiemap followed

> -     * by an array of struct fiemap_extent whose size is set

> -     * in fiemap->fm_extent_count. The array is filled in by the

> -     * ioctl.

> -     */

> -    int target_size_in, target_size_out;

> -    struct fiemap *fm;

> -    const argtype *arg_type = ie->arg_type;

> -    const argtype extent_arg_type[] = { MK_STRUCT(STRUCT_fiemap_extent) };

> -    void *argptr, *p;

> -    abi_long ret;

> -    int i, extent_size = thunk_type_size(extent_arg_type, 0);

> -    uint32_t outbufsz;

> -    int free_fm = 0;

> -

> -    assert(arg_type[0] == TYPE_PTR);

> -    assert(ie->access == IOC_RW);

> -    arg_type++;

> -    target_size_in = thunk_type_size(arg_type, 0);

> -    argptr = lock_user(VERIFY_READ, arg, target_size_in, 1);

> -    if (!argptr) {

> -        return -TARGET_EFAULT;

> -    }

> -    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> -    unlock_user(argptr, arg, 0);

> -    fm = (struct fiemap *)buf_temp;

> -    if (fm->fm_extent_count > FIEMAP_MAX_EXTENTS) {

> -        return -TARGET_EINVAL;

> -    }

> -

> -    outbufsz = sizeof (*fm) +

> -        (sizeof(struct fiemap_extent) * fm->fm_extent_count);

> -

> -    if (outbufsz > MAX_STRUCT_SIZE) {

> -        /* We can't fit all the extents into the fixed size buffer.

> -         * Allocate one that is large enough and use it instead.

> -         */

> -        fm = g_try_malloc(outbufsz);

> -        if (!fm) {

> -            return -TARGET_ENOMEM;

> -        }

> -        memcpy(fm, buf_temp, sizeof(struct fiemap));

> -        free_fm = 1;

> -    }

> -    ret = get_errno(safe_ioctl(fd, ie->host_cmd, fm));

> -    if (!is_error(ret)) {

> -        target_size_out = target_size_in;

> -        /* An extent_count of 0 means we were only counting the extents

> -         * so there are no structs to copy

> -         */

> -        if (fm->fm_extent_count != 0) {

> -            target_size_out += fm->fm_mapped_extents * extent_size;

> -        }

> -        argptr = lock_user(VERIFY_WRITE, arg, target_size_out, 0);

> -        if (!argptr) {

> -            ret = -TARGET_EFAULT;

> -        } else {

> -            /* Convert the struct fiemap */

> -            thunk_convert(argptr, fm, arg_type, THUNK_TARGET);

> -            if (fm->fm_extent_count != 0) {

> -                p = argptr + target_size_in;

> -                /* ...and then all the struct fiemap_extents */

> -                for (i = 0; i < fm->fm_mapped_extents; i++) {

> -                    thunk_convert(p, &fm->fm_extents[i], extent_arg_type,

> -                                  THUNK_TARGET);

> -                    p += extent_size;

> -                }

> -            }

> -            unlock_user(argptr, arg, target_size_out);

> -        }

> -    }

> -    if (free_fm) {

> -        g_free(fm);

> -    }

> -    return ret;

> -}

> -#endif

> -

> -static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,

> -                                int fd, int cmd, abi_long arg)

> -{

> -    const argtype *arg_type = ie->arg_type;

> -    int target_size;

> -    void *argptr;

> -    int ret;

> -    struct ifconf *host_ifconf;

> -    uint32_t outbufsz;

> -    const argtype ifreq_arg_type[] = { MK_STRUCT(STRUCT_sockaddr_ifreq) };

> -    int target_ifreq_size;

> -    int nb_ifreq;

> -    int free_buf = 0;

> -    int i;

> -    int target_ifc_len;

> -    abi_long target_ifc_buf;

> -    int host_ifc_len;

> -    char *host_ifc_buf;

> -

> -    assert(arg_type[0] == TYPE_PTR);

> -    assert(ie->access == IOC_RW);

> -

> -    arg_type++;

> -    target_size = thunk_type_size(arg_type, 0);

> -

> -    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> -    if (!argptr)

> -        return -TARGET_EFAULT;

> -    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> -    unlock_user(argptr, arg, 0);

> -

> -    host_ifconf = (struct ifconf *)(unsigned long)buf_temp;

> -    target_ifc_len = host_ifconf->ifc_len;

> -    target_ifc_buf = (abi_long)(unsigned long)host_ifconf->ifc_buf;

> -

> -    target_ifreq_size = thunk_type_size(ifreq_arg_type, 0);

> -    nb_ifreq = target_ifc_len / target_ifreq_size;

> -    host_ifc_len = nb_ifreq * sizeof(struct ifreq);

> -

> -    outbufsz = sizeof(*host_ifconf) + host_ifc_len;

> -    if (outbufsz > MAX_STRUCT_SIZE) {

> -        /* We can't fit all the extents into the fixed size buffer.

> -         * Allocate one that is large enough and use it instead.

> -         */

> -        host_ifconf = malloc(outbufsz);

> -        if (!host_ifconf) {

> -            return -TARGET_ENOMEM;

> -        }

> -        memcpy(host_ifconf, buf_temp, sizeof(*host_ifconf));

> -        free_buf = 1;

> -    }

> -    host_ifc_buf = (char*)host_ifconf + sizeof(*host_ifconf);

> -

> -    host_ifconf->ifc_len = host_ifc_len;

> -    host_ifconf->ifc_buf = host_ifc_buf;

> -

> -    ret = get_errno(safe_ioctl(fd, ie->host_cmd, host_ifconf));

> -    if (!is_error(ret)) {

> -	/* convert host ifc_len to target ifc_len */

> -

> -        nb_ifreq = host_ifconf->ifc_len / sizeof(struct ifreq);

> -        target_ifc_len = nb_ifreq * target_ifreq_size;

> -        host_ifconf->ifc_len = target_ifc_len;

> -

> -	/* restore target ifc_buf */

> -

> -        host_ifconf->ifc_buf = (char *)(unsigned long)target_ifc_buf;

> -

> -	/* copy struct ifconf to target user */

> -

> -        argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);

> -        if (!argptr)

> -            return -TARGET_EFAULT;

> -        thunk_convert(argptr, host_ifconf, arg_type, THUNK_TARGET);

> -        unlock_user(argptr, arg, target_size);

> -

> -	/* copy ifreq[] to target user */

> -

> -        argptr = lock_user(VERIFY_WRITE, target_ifc_buf, target_ifc_len, 0);

> -        for (i = 0; i < nb_ifreq ; i++) {

> -            thunk_convert(argptr + i * target_ifreq_size,

> -                          host_ifc_buf + i * sizeof(struct ifreq),

> -                          ifreq_arg_type, THUNK_TARGET);

> -        }

> -        unlock_user(argptr, target_ifc_buf, target_ifc_len);

> -    }

> -

> -    if (free_buf) {

> -        free(host_ifconf);

> -    }

> -

> -    return ret;

> -}

> -

> -#if defined(CONFIG_USBFS)

> -#if HOST_LONG_BITS > 64

> -#error USBDEVFS thunks do not support >64 bit hosts yet.

> -#endif

> -struct live_urb {

> -    uint64_t target_urb_adr;

> -    uint64_t target_buf_adr;

> -    char *target_buf_ptr;

> -    struct usbdevfs_urb host_urb;

> -};

> -

> -static GHashTable *usbdevfs_urb_hashtable(void)

> -{

> -    static GHashTable *urb_hashtable;

> -

> -    if (!urb_hashtable) {

> -        urb_hashtable = g_hash_table_new(g_int64_hash, g_int64_equal);

> -    }

> -    return urb_hashtable;

> -}

> -

> -static void urb_hashtable_insert(struct live_urb *urb)

> -{

> -    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();

> -    g_hash_table_insert(urb_hashtable, urb, urb);

> -}

> -

> -static struct live_urb *urb_hashtable_lookup(uint64_t target_urb_adr)

> -{

> -    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();

> -    return g_hash_table_lookup(urb_hashtable, &target_urb_adr);

> -}

> -

> -static void urb_hashtable_remove(struct live_urb *urb)

> -{

> -    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();

> -    g_hash_table_remove(urb_hashtable, urb);

> -}

> -

> -static abi_long

> -do_ioctl_usbdevfs_reapurb(const IOCTLEntry *ie, uint8_t *buf_temp,

> -                          int fd, int cmd, abi_long arg)

> -{

> -    const argtype usbfsurb_arg_type[] = { MK_STRUCT(STRUCT_usbdevfs_urb) };

> -    const argtype ptrvoid_arg_type[] = { TYPE_PTRVOID, 0, 0 };

> -    struct live_urb *lurb;

> -    void *argptr;

> -    uint64_t hurb;

> -    int target_size;

> -    uintptr_t target_urb_adr;

> -    abi_long ret;

> -

> -    target_size = thunk_type_size(usbfsurb_arg_type, THUNK_TARGET);

> -

> -    memset(buf_temp, 0, sizeof(uint64_t));

> -    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> -    if (is_error(ret)) {

> -        return ret;

> -    }

> -

> -    memcpy(&hurb, buf_temp, sizeof(uint64_t));

> -    lurb = (void *)((uintptr_t)hurb - offsetof(struct live_urb, host_urb));

> -    if (!lurb->target_urb_adr) {

> -        return -TARGET_EFAULT;

> -    }

> -    urb_hashtable_remove(lurb);

> -    unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr,

> -        lurb->host_urb.buffer_length);

> -    lurb->target_buf_ptr = NULL;

> -

> -    /* restore the guest buffer pointer */

> -    lurb->host_urb.buffer = (void *)(uintptr_t)lurb->target_buf_adr;

> -

> -    /* update the guest urb struct */

> -    argptr = lock_user(VERIFY_WRITE, lurb->target_urb_adr, target_size, 0);

> -    if (!argptr) {

> -        g_free(lurb);

> -        return -TARGET_EFAULT;

> -    }

> -    thunk_convert(argptr, &lurb->host_urb, usbfsurb_arg_type, THUNK_TARGET);

> -    unlock_user(argptr, lurb->target_urb_adr, target_size);

> -

> -    target_size = thunk_type_size(ptrvoid_arg_type, THUNK_TARGET);

> -    /* write back the urb handle */

> -    argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);

> -    if (!argptr) {

> -        g_free(lurb);

> -        return -TARGET_EFAULT;

> -    }

> -

> -    /* GHashTable uses 64-bit keys but thunk_convert expects uintptr_t */

> -    target_urb_adr = lurb->target_urb_adr;

> -    thunk_convert(argptr, &target_urb_adr, ptrvoid_arg_type, THUNK_TARGET);

> -    unlock_user(argptr, arg, target_size);

> -

> -    g_free(lurb);

> -    return ret;

> -}

> -

> -static abi_long

> -do_ioctl_usbdevfs_discardurb(const IOCTLEntry *ie,

> -                             uint8_t *buf_temp __attribute__((unused)),

> -                             int fd, int cmd, abi_long arg)

> -{

> -    struct live_urb *lurb;

> -

> -    /* map target address back to host URB with metadata. */

> -    lurb = urb_hashtable_lookup(arg);

> -    if (!lurb) {

> -        return -TARGET_EFAULT;

> -    }

> -    return get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));

> -}

> -

> -static abi_long

> -do_ioctl_usbdevfs_submiturb(const IOCTLEntry *ie, uint8_t *buf_temp,

> -                            int fd, int cmd, abi_long arg)

> -{

> -    const argtype *arg_type = ie->arg_type;

> -    int target_size;

> -    abi_long ret;

> -    void *argptr;

> -    int rw_dir;

> -    struct live_urb *lurb;

> -

> -    /*

> -     * each submitted URB needs to map to a unique ID for the

> -     * kernel, and that unique ID needs to be a pointer to

> -     * host memory.  hence, we need to malloc for each URB.

> -     * isochronous transfers have a variable length struct.

> -     */

> -    arg_type++;

> -    target_size = thunk_type_size(arg_type, THUNK_TARGET);

> -

> -    /* construct host copy of urb and metadata */

> -    lurb = g_try_malloc0(sizeof(struct live_urb));

> -    if (!lurb) {

> -        return -TARGET_ENOMEM;

> -    }

> -

> -    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> -    if (!argptr) {

> -        g_free(lurb);

> -        return -TARGET_EFAULT;

> -    }

> -    thunk_convert(&lurb->host_urb, argptr, arg_type, THUNK_HOST);

> -    unlock_user(argptr, arg, 0);

> -

> -    lurb->target_urb_adr = arg;

> -    lurb->target_buf_adr = (uintptr_t)lurb->host_urb.buffer;

> -

> -    /* buffer space used depends on endpoint type so lock the entire buffer */

> -    /* control type urbs should check the buffer contents for true direction */

> -    rw_dir = lurb->host_urb.endpoint & USB_DIR_IN ? VERIFY_WRITE : VERIFY_READ;

> -    lurb->target_buf_ptr = lock_user(rw_dir, lurb->target_buf_adr,

> -        lurb->host_urb.buffer_length, 1);

> -    if (lurb->target_buf_ptr == NULL) {

> -        g_free(lurb);

> -        return -TARGET_EFAULT;

> -    }

> -

> -    /* update buffer pointer in host copy */

> -    lurb->host_urb.buffer = lurb->target_buf_ptr;

> -

> -    ret = get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));

> -    if (is_error(ret)) {

> -        unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr, 0);

> -        g_free(lurb);

> -    } else {

> -        urb_hashtable_insert(lurb);

> -    }

> -

> -    return ret;

> -}

> -#endif /* CONFIG_USBFS */

> -

> -static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,

> -                            int cmd, abi_long arg)

> -{

> -    void *argptr;

> -    struct dm_ioctl *host_dm;

> -    abi_long guest_data;

> -    uint32_t guest_data_size;

> -    int target_size;

> -    const argtype *arg_type = ie->arg_type;

> -    abi_long ret;

> -    void *big_buf = NULL;

> -    char *host_data;

> -

> -    arg_type++;

> -    target_size = thunk_type_size(arg_type, 0);

> -    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> -    if (!argptr) {

> -        ret = -TARGET_EFAULT;

> -        goto out;

> -    }

> -    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> -    unlock_user(argptr, arg, 0);

> -

> -    /* buf_temp is too small, so fetch things into a bigger buffer */

> -    big_buf = g_malloc0(((struct dm_ioctl*)buf_temp)->data_size * 2);

> -    memcpy(big_buf, buf_temp, target_size);

> -    buf_temp = big_buf;

> -    host_dm = big_buf;

> -

> -    guest_data = arg + host_dm->data_start;

> -    if ((guest_data - arg) < 0) {

> -        ret = -TARGET_EINVAL;

> -        goto out;

> -    }

> -    guest_data_size = host_dm->data_size - host_dm->data_start;

> -    host_data = (char*)host_dm + host_dm->data_start;

> -

> -    argptr = lock_user(VERIFY_READ, guest_data, guest_data_size, 1);

> -    if (!argptr) {

> -        ret = -TARGET_EFAULT;

> -        goto out;

> -    }

> -

> -    switch (ie->host_cmd) {

> -    case DM_REMOVE_ALL:

> -    case DM_LIST_DEVICES:

> -    case DM_DEV_CREATE:

> -    case DM_DEV_REMOVE:

> -    case DM_DEV_SUSPEND:

> -    case DM_DEV_STATUS:

> -    case DM_DEV_WAIT:

> -    case DM_TABLE_STATUS:

> -    case DM_TABLE_CLEAR:

> -    case DM_TABLE_DEPS:

> -    case DM_LIST_VERSIONS:

> -        /* no input data */

> -        break;

> -    case DM_DEV_RENAME:

> -    case DM_DEV_SET_GEOMETRY:

> -        /* data contains only strings */

> -        memcpy(host_data, argptr, guest_data_size);

> -        break;

> -    case DM_TARGET_MSG:

> -        memcpy(host_data, argptr, guest_data_size);

> -        *(uint64_t*)host_data = tswap64(*(uint64_t*)argptr);

> -        break;

> -    case DM_TABLE_LOAD:

> -    {

> -        void *gspec = argptr;

> -        void *cur_data = host_data;

> -        const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };

> -        int spec_size = thunk_type_size(arg_type, 0);

> -        int i;

> -

> -        for (i = 0; i < host_dm->target_count; i++) {

> -            struct dm_target_spec *spec = cur_data;

> -            uint32_t next;

> -            int slen;

> -

> -            thunk_convert(spec, gspec, arg_type, THUNK_HOST);

> -            slen = strlen((char*)gspec + spec_size) + 1;

> -            next = spec->next;

> -            spec->next = sizeof(*spec) + slen;

> -            strcpy((char*)&spec[1], gspec + spec_size);

> -            gspec += next;

> -            cur_data += spec->next;

> -        }

> -        break;

> -    }

> -    default:

> -        ret = -TARGET_EINVAL;

> -        unlock_user(argptr, guest_data, 0);

> -        goto out;

> -    }

> -    unlock_user(argptr, guest_data, 0);

> -

> -    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> -    if (!is_error(ret)) {

> -        guest_data = arg + host_dm->data_start;

> -        guest_data_size = host_dm->data_size - host_dm->data_start;

> -        argptr = lock_user(VERIFY_WRITE, guest_data, guest_data_size, 0);

> -        switch (ie->host_cmd) {

> -        case DM_REMOVE_ALL:

> -        case DM_DEV_CREATE:

> -        case DM_DEV_REMOVE:

> -        case DM_DEV_RENAME:

> -        case DM_DEV_SUSPEND:

> -        case DM_DEV_STATUS:

> -        case DM_TABLE_LOAD:

> -        case DM_TABLE_CLEAR:

> -        case DM_TARGET_MSG:

> -        case DM_DEV_SET_GEOMETRY:

> -            /* no return data */

> -            break;

> -        case DM_LIST_DEVICES:

> -        {

> -            struct dm_name_list *nl = (void*)host_dm + host_dm->data_start;

> -            uint32_t remaining_data = guest_data_size;

> -            void *cur_data = argptr;

> -            const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) };

> -            int nl_size = 12; /* can't use thunk_size due to alignment */

> -

> -            while (1) {

> -                uint32_t next = nl->next;

> -                if (next) {

> -                    nl->next = nl_size + (strlen(nl->name) + 1);

> -                }

> -                if (remaining_data < nl->next) {

> -                    host_dm->flags |= DM_BUFFER_FULL_FLAG;

> -                    break;

> -                }

> -                thunk_convert(cur_data, nl, arg_type, THUNK_TARGET);

> -                strcpy(cur_data + nl_size, nl->name);

> -                cur_data += nl->next;

> -                remaining_data -= nl->next;

> -                if (!next) {

> -                    break;

> -                }

> -                nl = (void*)nl + next;

> -            }

> -            break;

> -        }

> -        case DM_DEV_WAIT:

> -        case DM_TABLE_STATUS:

> -        {

> -            struct dm_target_spec *spec = (void*)host_dm + host_dm->data_start;

> -            void *cur_data = argptr;

> -            const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };

> -            int spec_size = thunk_type_size(arg_type, 0);

> -            int i;

> -

> -            for (i = 0; i < host_dm->target_count; i++) {

> -                uint32_t next = spec->next;

> -                int slen = strlen((char*)&spec[1]) + 1;

> -                spec->next = (cur_data - argptr) + spec_size + slen;

> -                if (guest_data_size < spec->next) {

> -                    host_dm->flags |= DM_BUFFER_FULL_FLAG;

> -                    break;

> -                }

> -                thunk_convert(cur_data, spec, arg_type, THUNK_TARGET);

> -                strcpy(cur_data + spec_size, (char*)&spec[1]);

> -                cur_data = argptr + spec->next;

> -                spec = (void*)host_dm + host_dm->data_start + next;

> -            }

> -            break;

> -        }

> -        case DM_TABLE_DEPS:

> -        {

> -            void *hdata = (void*)host_dm + host_dm->data_start;

> -            int count = *(uint32_t*)hdata;

> -            uint64_t *hdev = hdata + 8;

> -            uint64_t *gdev = argptr + 8;

> -            int i;

> -

> -            *(uint32_t*)argptr = tswap32(count);

> -            for (i = 0; i < count; i++) {

> -                *gdev = tswap64(*hdev);

> -                gdev++;

> -                hdev++;

> -            }

> -            break;

> -        }

> -        case DM_LIST_VERSIONS:

> -        {

> -            struct dm_target_versions *vers = (void*)host_dm + host_dm->data_start;

> -            uint32_t remaining_data = guest_data_size;

> -            void *cur_data = argptr;

> -            const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_versions) };

> -            int vers_size = thunk_type_size(arg_type, 0);

> -

> -            while (1) {

> -                uint32_t next = vers->next;

> -                if (next) {

> -                    vers->next = vers_size + (strlen(vers->name) + 1);

> -                }

> -                if (remaining_data < vers->next) {

> -                    host_dm->flags |= DM_BUFFER_FULL_FLAG;

> -                    break;

> -                }

> -                thunk_convert(cur_data, vers, arg_type, THUNK_TARGET);

> -                strcpy(cur_data + vers_size, vers->name);

> -                cur_data += vers->next;

> -                remaining_data -= vers->next;

> -                if (!next) {

> -                    break;

> -                }

> -                vers = (void*)vers + next;

> -            }

> -            break;

> -        }

> -        default:

> -            unlock_user(argptr, guest_data, 0);

> -            ret = -TARGET_EINVAL;

> -            goto out;

> -        }

> -        unlock_user(argptr, guest_data, guest_data_size);

> -

> -        argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);

> -        if (!argptr) {

> -            ret = -TARGET_EFAULT;

> -            goto out;

> -        }

> -        thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);

> -        unlock_user(argptr, arg, target_size);

> -    }

> -out:

> -    g_free(big_buf);

> -    return ret;

> -}

> -

> -static abi_long do_ioctl_blkpg(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,

> -                               int cmd, abi_long arg)

> -{

> -    void *argptr;

> -    int target_size;

> -    const argtype *arg_type = ie->arg_type;

> -    const argtype part_arg_type[] = { MK_STRUCT(STRUCT_blkpg_partition) };

> -    abi_long ret;

> -

> -    struct blkpg_ioctl_arg *host_blkpg = (void*)buf_temp;

> -    struct blkpg_partition host_part;

> -

> -    /* Read and convert blkpg */

> -    arg_type++;

> -    target_size = thunk_type_size(arg_type, 0);

> -    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> -    if (!argptr) {

> -        ret = -TARGET_EFAULT;

> -        goto out;

> -    }

> -    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> -    unlock_user(argptr, arg, 0);

> -

> -    switch (host_blkpg->op) {

> -    case BLKPG_ADD_PARTITION:

> -    case BLKPG_DEL_PARTITION:

> -        /* payload is struct blkpg_partition */

> -        break;

> -    default:

> -        /* Unknown opcode */

> -        ret = -TARGET_EINVAL;

> -        goto out;

> -    }

> -

> -    /* Read and convert blkpg->data */

> -    arg = (abi_long)(uintptr_t)host_blkpg->data;

> -    target_size = thunk_type_size(part_arg_type, 0);

> -    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> -    if (!argptr) {

> -        ret = -TARGET_EFAULT;

> -        goto out;

> -    }

> -    thunk_convert(&host_part, argptr, part_arg_type, THUNK_HOST);

> -    unlock_user(argptr, arg, 0);

> -

> -    /* Swizzle the data pointer to our local copy and call! */

> -    host_blkpg->data = &host_part;

> -    ret = get_errno(safe_ioctl(fd, ie->host_cmd, host_blkpg));

> -

> -out:

> -    return ret;

> -}

> -

> -static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp,

> -                                int fd, int cmd, abi_long arg)

> -{

> -    const argtype *arg_type = ie->arg_type;

> -    const StructEntry *se;

> -    const argtype *field_types;

> -    const int *dst_offsets, *src_offsets;

> -    int target_size;

> -    void *argptr;

> -    abi_ulong *target_rt_dev_ptr;

> -    unsigned long *host_rt_dev_ptr;

> -    abi_long ret;

> -    int i;

> -

> -    assert(ie->access == IOC_W);

> -    assert(*arg_type == TYPE_PTR);

> -    arg_type++;

> -    assert(*arg_type == TYPE_STRUCT);

> -    target_size = thunk_type_size(arg_type, 0);

> -    argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> -    if (!argptr) {

> -        return -TARGET_EFAULT;

> -    }

> -    arg_type++;

> -    assert(*arg_type == (int)STRUCT_rtentry);

> -    se = struct_entries + *arg_type++;

> -    assert(se->convert[0] == NULL);

> -    /* convert struct here to be able to catch rt_dev string */

> -    field_types = se->field_types;

> -    dst_offsets = se->field_offsets[THUNK_HOST];

> -    src_offsets = se->field_offsets[THUNK_TARGET];

> -    for (i = 0; i < se->nb_fields; i++) {

> -        if (dst_offsets[i] == offsetof(struct rtentry, rt_dev)) {

> -            assert(*field_types == TYPE_PTRVOID);

> -            target_rt_dev_ptr = (abi_ulong *)(argptr + src_offsets[i]);

> -            host_rt_dev_ptr = (unsigned long *)(buf_temp + dst_offsets[i]);

> -            if (*target_rt_dev_ptr != 0) {

> -                *host_rt_dev_ptr = (unsigned long)lock_user_string(

> -                                                  tswapal(*target_rt_dev_ptr));

> -                if (!*host_rt_dev_ptr) {

> -                    unlock_user(argptr, arg, 0);

> -                    return -TARGET_EFAULT;

> -                }

> -            } else {

> -                *host_rt_dev_ptr = 0;

> -            }

> -            field_types++;

> -            continue;

> -        }

> -        field_types = thunk_convert(buf_temp + dst_offsets[i],

> -                                    argptr + src_offsets[i],

> -                                    field_types, THUNK_HOST);

> -    }

> -    unlock_user(argptr, arg, 0);

> -

> -    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> -    if (*host_rt_dev_ptr != 0) {

> -        unlock_user((void *)*host_rt_dev_ptr,

> -                    *target_rt_dev_ptr, 0);

> -    }

> -    return ret;

> -}

> -

> -static abi_long do_ioctl_kdsigaccept(const IOCTLEntry *ie, uint8_t *buf_temp,

> -                                     int fd, int cmd, abi_long arg)

> -{

> -    int sig = target_to_host_signal(arg);

> -    return get_errno(safe_ioctl(fd, ie->host_cmd, sig));

> -}

> -

> -#ifdef TIOCGPTPEER

> -static abi_long do_ioctl_tiocgptpeer(const IOCTLEntry *ie, uint8_t *buf_temp,

> -                                     int fd, int cmd, abi_long arg)

> -{

> -    int flags = target_to_host_bitmask(arg, fcntl_flags_tbl);

> -    return get_errno(safe_ioctl(fd, ie->host_cmd, flags));

> -}

> -#endif

> -

> -static IOCTLEntry ioctl_entries[] = {

> -#define IOCTL(cmd, access, ...) \

> -    { TARGET_ ## cmd, cmd, #cmd, access, 0, {  __VA_ARGS__ } },

> -#define IOCTL_SPECIAL(cmd, access, dofn, ...)                      \

> -    { TARGET_ ## cmd, cmd, #cmd, access, dofn, {  __VA_ARGS__ } },

> -#define IOCTL_IGNORE(cmd) \

> -    { TARGET_ ## cmd, 0, #cmd },

> -#include "ioctls.h"

> -    { 0, 0, },

> -};

> -

> -/* ??? Implement proper locking for ioctls.  */

> -/* do_ioctl() Must return target values and target errnos. */

> -static abi_long do_ioctl(int fd, int cmd, abi_long arg)

> -{

> -    const IOCTLEntry *ie;

> -    const argtype *arg_type;

> -    abi_long ret;

> -    uint8_t buf_temp[MAX_STRUCT_SIZE];

> -    int target_size;

> -    void *argptr;

> -

> -    ie = ioctl_entries;

> -    for(;;) {

> -        if (ie->target_cmd == 0) {

> -            gemu_log("Unsupported ioctl: cmd=0x%04lx\n", (long)cmd);

> -            return -TARGET_ENOSYS;

> -        }

> -        if (ie->target_cmd == cmd)

> -            break;

> -        ie++;

> -    }

> -    arg_type = ie->arg_type;

> -    if (ie->do_ioctl) {

> -        return ie->do_ioctl(ie, buf_temp, fd, cmd, arg);

> -    } else if (!ie->host_cmd) {

> -        /* Some architectures define BSD ioctls in their headers

> -           that are not implemented in Linux.  */

> -        return -TARGET_ENOSYS;

> -    }

> -

> -    switch(arg_type[0]) {

> -    case TYPE_NULL:

> -        /* no argument */

> -        ret = get_errno(safe_ioctl(fd, ie->host_cmd));

> -        break;

> -    case TYPE_PTRVOID:

> -    case TYPE_INT:

> -        ret = get_errno(safe_ioctl(fd, ie->host_cmd, arg));

> -        break;

> -    case TYPE_PTR:

> -        arg_type++;

> -        target_size = thunk_type_size(arg_type, 0);

> -        switch(ie->access) {

> -        case IOC_R:

> -            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> -            if (!is_error(ret)) {

> -                argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);

> -                if (!argptr)

> -                    return -TARGET_EFAULT;

> -                thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);

> -                unlock_user(argptr, arg, target_size);

> -            }

> -            break;

> -        case IOC_W:

> -            argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> -            if (!argptr)

> -                return -TARGET_EFAULT;

> -            thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> -            unlock_user(argptr, arg, 0);

> -            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> -            break;

> -        default:

> -        case IOC_RW:

> -            argptr = lock_user(VERIFY_READ, arg, target_size, 1);

> -            if (!argptr)

> -                return -TARGET_EFAULT;

> -            thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);

> -            unlock_user(argptr, arg, 0);

> -            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));

> -            if (!is_error(ret)) {

> -                argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);

> -                if (!argptr)

> -                    return -TARGET_EFAULT;

> -                thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);

> -                unlock_user(argptr, arg, target_size);

> -            }

> -            break;

> -        }

> -        break;

> -    default:

> -        gemu_log("Unsupported ioctl type: cmd=0x%04lx type=%d\n",

> -                 (long)cmd, arg_type[0]);

> -        ret = -TARGET_ENOSYS;

> -        break;

> -    }

> -    return ret;

> -}

> -

>  static const bitmask_transtbl iflag_tbl[] = {

>          { TARGET_IGNBRK, TARGET_IGNBRK, IGNBRK, IGNBRK },

>          { TARGET_BRKINT, TARGET_BRKINT, BRKINT, BRKINT },

> @@ -5241,8 +4401,6 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,

>      void *p;

>  

>      switch(num) {

> -    case TARGET_NR_ioctl:

> -        return do_ioctl(arg1, arg2, arg3);

>  #ifdef TARGET_NR_fcntl

>      case TARGET_NR_fcntl:

>          return do_fcntl(arg1, arg2, arg3);

> @@ -8811,6 +7969,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,

>                                  int64_t arg5, int64_t arg6)

>  

>  #include "syscall-file.inc.c"

> +#include "syscall-ioctl.inc.c"

>  #include "syscall-ipc.inc.c"

>  #include "syscall-mem.inc.c"

>  #include "syscall-proc.inc.c"

> diff --git a/linux-user/strace.list b/linux-user/strace.list

> index 9f2f8977b4..15208b5349 100644

> --- a/linux-user/strace.list

> +++ b/linux-user/strace.list

> @@ -365,9 +365,6 @@

>  #ifdef TARGET_NR_io_cancel

>  { TARGET_NR_io_cancel, "io_cancel" , NULL, NULL, NULL },

>  #endif

> -#ifdef TARGET_NR_ioctl

> -{ TARGET_NR_ioctl, "ioctl" , NULL, NULL, NULL },

> -#endif

>  #ifdef TARGET_NR_io_destroy

>  { TARGET_NR_io_destroy, "io_destroy" , NULL, NULL, NULL },

>  #endif

>
Laurent Vivier Feb. 13, 2019, 1:46 p.m. UTC | #2
On 13/02/2019 14:09, Laurent Vivier wrote:
> Hi,

> 

> this one is really a cut'n'paste but it introduces a problem with 

> qemu-alpha, I don't know how/why:

> 

>   $ sudo unshare --ipc --uts --pid --fork --kill-child --mount chroot chroot/alpha/sid/

>   Unsupported ioctl: cmd=0x80047476

> 

> And then it hangs.

> 

> I'll try to debug.


In fact it happens with sid, with ppc64 too, but not with m68k and sh4.

And only with "unshare --pid --fork".

Without "unshare", the result is:

  Unsupported ioctl: cmd=0x80047476
  bash: cannot set terminal process group (11507): Function not implemented
  bash: no job control in this shell
  Unsupported ioctl: cmd=0x80087467
  Unsupported ioctl: cmd=0x80087467
  Unsupported ioctl: cmd=0x802c7415

Thanks,
Laurent
Richard Henderson April 9, 2019, 11:15 p.m. UTC | #3
On 2/13/19 3:46 AM, Laurent Vivier wrote:
> On 13/02/2019 14:09, Laurent Vivier wrote:

>> Hi,

>>

>> this one is really a cut'n'paste but it introduces a problem with 

>> qemu-alpha, I don't know how/why:

>>

>>   $ sudo unshare --ipc --uts --pid --fork --kill-child --mount chroot chroot/alpha/sid/

>>   Unsupported ioctl: cmd=0x80047476

>>

>> And then it hangs.

>>

>> I'll try to debug.

> 

> In fact it happens with sid, with ppc64 too, but not with m68k and sh4.

> 

> And only with "unshare --pid --fork".

> 

> Without "unshare", the result is:

> 

>   Unsupported ioctl: cmd=0x80047476

>   bash: cannot set terminal process group (11507): Function not implemented

>   bash: no job control in this shell

>   Unsupported ioctl: cmd=0x80087467

>   Unsupported ioctl: cmd=0x80087467

>   Unsupported ioctl: cmd=0x802c7415


I'm just now getting back to this, and cannot replicate the issue, at least
rebased onto master.  With or without unshare, I see your "without unshare"
results.

The rebase onto master is at

  https://github.com/rth7680/qemu.git lu-split-6


r~
Richard Henderson April 9, 2019, 11:30 p.m. UTC | #4
On 4/9/19 1:15 PM, Richard Henderson wrote:
> On 2/13/19 3:46 AM, Laurent Vivier wrote:

>> In fact it happens with sid, with ppc64 too, but not with m68k and sh4.

>>

>> And only with "unshare --pid --fork".

>>

>> Without "unshare", the result is:

>>

>>   Unsupported ioctl: cmd=0x80047476

>>   bash: cannot set terminal process group (11507): Function not implemented

>>   bash: no job control in this shell

>>   Unsupported ioctl: cmd=0x80087467

>>   Unsupported ioctl: cmd=0x80087467

>>   Unsupported ioctl: cmd=0x802c7415

> 

> I'm just now getting back to this, and cannot replicate the issue


... Oh nevermind.  I should be expecting no "unsupported" at all, since that's
what we get with master.


r~
Richard Henderson April 10, 2019, 1:55 a.m. UTC | #5
On 4/9/19 1:30 PM, Richard Henderson wrote:
> On 4/9/19 1:15 PM, Richard Henderson wrote:

>> On 2/13/19 3:46 AM, Laurent Vivier wrote:

>>> In fact it happens with sid, with ppc64 too, but not with m68k and sh4.

>>>

>>> And only with "unshare --pid --fork".

>>>

>>> Without "unshare", the result is:

>>>

>>>   Unsupported ioctl: cmd=0x80047476

>>>   bash: cannot set terminal process group (11507): Function not implemented

>>>   bash: no job control in this shell

>>>   Unsupported ioctl: cmd=0x80087467

>>>   Unsupported ioctl: cmd=0x80087467

>>>   Unsupported ioctl: cmd=0x802c7415

>>

>> I'm just now getting back to this, and cannot replicate the issue

> 

> ... Oh nevermind.  I should be expecting no "unsupported" at all, since that's

> what we get with master.


The cause is that "cmd" accidentally changed from "int" in the do_ioctl
parameter list to "abi_ulong" as a local variable in impl_ioctl.  This caused a
table lookup failure on any ioctl with bit 31 set, for 64-bit guests.


r~
Laurent Vivier May 9, 2019, 3:44 p.m. UTC | #6
On 10/04/2019 03:55, Richard Henderson wrote:
> On 4/9/19 1:30 PM, Richard Henderson wrote:

>> On 4/9/19 1:15 PM, Richard Henderson wrote:

>>> On 2/13/19 3:46 AM, Laurent Vivier wrote:

>>>> In fact it happens with sid, with ppc64 too, but not with m68k and sh4.

>>>>

>>>> And only with "unshare --pid --fork".

>>>>

>>>> Without "unshare", the result is:

>>>>

>>>>    Unsupported ioctl: cmd=0x80047476

>>>>    bash: cannot set terminal process group (11507): Function not implemented

>>>>    bash: no job control in this shell

>>>>    Unsupported ioctl: cmd=0x80087467

>>>>    Unsupported ioctl: cmd=0x80087467

>>>>    Unsupported ioctl: cmd=0x802c7415

>>>

>>> I'm just now getting back to this, and cannot replicate the issue

>>

>> ... Oh nevermind.  I should be expecting no "unsupported" at all, since that's

>> what we get with master.

> 

> The cause is that "cmd" accidentally changed from "int" in the do_ioctl

> parameter list to "abi_ulong" as a local variable in impl_ioctl.  This caused a

> table lookup failure on any ioctl with bit 31 set, for 64-bit guests.


Do you plan to send a new version of this series?

Thanks,
Laurent
Richard Henderson May 9, 2019, 3:54 p.m. UTC | #7
On 5/9/19 8:44 AM, Laurent Vivier wrote:
> Do you plan to send a new version of this series?


Yes.  Soon(tm).


r~
diff mbox series

Patch

diff --git a/linux-user/syscall-defs.h b/linux-user/syscall-defs.h
index f8f280f376..f58b9745a4 100644
--- a/linux-user/syscall-defs.h
+++ b/linux-user/syscall-defs.h
@@ -61,6 +61,7 @@  SYSCALL_DEF(getppid);
 #ifdef TARGET_NR_getxpid
 SYSCALL_DEF(getxpid);
 #endif
+SYSCALL_DEF(ioctl, ARG_DEC, ARG_HEX);
 #ifdef TARGET_NR_ipc
 SYSCALL_DEF_ARGS(ipc, ARG_HEX, ARG_DEC, ARG_DEC, ARG_HEX, ARG_PTR, ARG_HEX);
 #endif
diff --git a/linux-user/syscall-ioctl.inc.c b/linux-user/syscall-ioctl.inc.c
new file mode 100644
index 0000000000..820994105f
--- /dev/null
+++ b/linux-user/syscall-ioctl.inc.c
@@ -0,0 +1,873 @@ 
+/*
+ *  Linux ioctl syscall implementation
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ *  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/>.
+ */
+
+typedef struct IOCTLEntry IOCTLEntry;
+
+typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp,
+                             int fd, int cmd, abi_long arg);
+
+struct IOCTLEntry {
+    int target_cmd;
+    unsigned int host_cmd;
+    const char *name;
+    int access;
+    do_ioctl_fn *do_ioctl;
+    const argtype arg_type[5];
+};
+
+#define IOC_R 0x0001
+#define IOC_W 0x0002
+#define IOC_RW (IOC_R | IOC_W)
+
+#define MAX_STRUCT_SIZE 4096
+
+#ifdef CONFIG_FIEMAP
+/*
+ * So fiemap access checks don't overflow on 32 bit systems.
+ * This is very slightly smaller than the limit imposed by
+ * the underlying kernel.
+ */
+#define FIEMAP_MAX_EXTENTS ((UINT_MAX - sizeof(struct fiemap))  \
+                            / sizeof(struct fiemap_extent))
+
+static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp,
+                                       int fd, int cmd, abi_long arg)
+{
+    /*
+     * The parameter for this ioctl is a struct fiemap followed
+     * by an array of struct fiemap_extent whose size is set
+     * in fiemap->fm_extent_count. The array is filled in by the
+     * ioctl.
+     */
+    int target_size_in, target_size_out;
+    struct fiemap *fm;
+    const argtype *arg_type = ie->arg_type;
+    const argtype extent_arg_type[] = { MK_STRUCT(STRUCT_fiemap_extent) };
+    void *argptr, *p;
+    abi_long ret;
+    int i, extent_size = thunk_type_size(extent_arg_type, 0);
+    uint32_t outbufsz;
+    int free_fm = 0;
+
+    assert(arg_type[0] == TYPE_PTR);
+    assert(ie->access == IOC_RW);
+    arg_type++;
+    target_size_in = thunk_type_size(arg_type, 0);
+    argptr = lock_user(VERIFY_READ, arg, target_size_in, 1);
+    if (!argptr) {
+        return -TARGET_EFAULT;
+    }
+    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+    unlock_user(argptr, arg, 0);
+    fm = (struct fiemap *)buf_temp;
+    if (fm->fm_extent_count > FIEMAP_MAX_EXTENTS) {
+        return -TARGET_EINVAL;
+    }
+
+    outbufsz = sizeof(*fm) + sizeof(struct fiemap_extent) * fm->fm_extent_count;
+
+    if (outbufsz > MAX_STRUCT_SIZE) {
+        /*
+         * We can't fit all the extents into the fixed size buffer.
+         * Allocate one that is large enough and use it instead.
+         */
+        fm = g_try_malloc(outbufsz);
+        if (!fm) {
+            return -TARGET_ENOMEM;
+        }
+        memcpy(fm, buf_temp, sizeof(struct fiemap));
+        free_fm = 1;
+    }
+    ret = get_errno(safe_ioctl(fd, ie->host_cmd, fm));
+    if (!is_error(ret)) {
+        target_size_out = target_size_in;
+        /*
+         * An extent_count of 0 means we were only counting the extents
+         * so there are no structs to copy
+         */
+        if (fm->fm_extent_count != 0) {
+            target_size_out += fm->fm_mapped_extents * extent_size;
+        }
+        argptr = lock_user(VERIFY_WRITE, arg, target_size_out, 0);
+        if (!argptr) {
+            ret = -TARGET_EFAULT;
+        } else {
+            /* Convert the struct fiemap */
+            thunk_convert(argptr, fm, arg_type, THUNK_TARGET);
+            if (fm->fm_extent_count != 0) {
+                p = argptr + target_size_in;
+                /* ...and then all the struct fiemap_extents */
+                for (i = 0; i < fm->fm_mapped_extents; i++) {
+                    thunk_convert(p, &fm->fm_extents[i], extent_arg_type,
+                                  THUNK_TARGET);
+                    p += extent_size;
+                }
+            }
+            unlock_user(argptr, arg, target_size_out);
+        }
+    }
+    if (free_fm) {
+        g_free(fm);
+    }
+    return ret;
+}
+#endif
+
+static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,
+                                int fd, int cmd, abi_long arg)
+{
+    const argtype *arg_type = ie->arg_type;
+    int target_size;
+    void *argptr;
+    int ret;
+    struct ifconf *host_ifconf;
+    uint32_t outbufsz;
+    const argtype ifreq_arg_type[] = { MK_STRUCT(STRUCT_sockaddr_ifreq) };
+    int target_ifreq_size;
+    int nb_ifreq;
+    int free_buf = 0;
+    int i;
+    int target_ifc_len;
+    abi_long target_ifc_buf;
+    int host_ifc_len;
+    char *host_ifc_buf;
+
+    assert(arg_type[0] == TYPE_PTR);
+    assert(ie->access == IOC_RW);
+
+    arg_type++;
+    target_size = thunk_type_size(arg_type, 0);
+
+    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+    if (!argptr) {
+        return -TARGET_EFAULT;
+    }
+    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+    unlock_user(argptr, arg, 0);
+
+    host_ifconf = (struct ifconf *)(unsigned long)buf_temp;
+    target_ifc_len = host_ifconf->ifc_len;
+    target_ifc_buf = (abi_long)(unsigned long)host_ifconf->ifc_buf;
+
+    target_ifreq_size = thunk_type_size(ifreq_arg_type, 0);
+    nb_ifreq = target_ifc_len / target_ifreq_size;
+    host_ifc_len = nb_ifreq * sizeof(struct ifreq);
+
+    outbufsz = sizeof(*host_ifconf) + host_ifc_len;
+    if (outbufsz > MAX_STRUCT_SIZE) {
+        /*
+         * We can't fit all the extents into the fixed size buffer.
+         * Allocate one that is large enough and use it instead.
+         */
+        host_ifconf = malloc(outbufsz);
+        if (!host_ifconf) {
+            return -TARGET_ENOMEM;
+        }
+        memcpy(host_ifconf, buf_temp, sizeof(*host_ifconf));
+        free_buf = 1;
+    }
+    host_ifc_buf = (char *)host_ifconf + sizeof(*host_ifconf);
+
+    host_ifconf->ifc_len = host_ifc_len;
+    host_ifconf->ifc_buf = host_ifc_buf;
+
+    ret = get_errno(safe_ioctl(fd, ie->host_cmd, host_ifconf));
+    if (!is_error(ret)) {
+        /* convert host ifc_len to target ifc_len */
+
+        nb_ifreq = host_ifconf->ifc_len / sizeof(struct ifreq);
+        target_ifc_len = nb_ifreq * target_ifreq_size;
+        host_ifconf->ifc_len = target_ifc_len;
+
+        /* restore target ifc_buf */
+
+        host_ifconf->ifc_buf = (char *)(unsigned long)target_ifc_buf;
+
+        /* copy struct ifconf to target user */
+
+        argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
+        if (!argptr) {
+            return -TARGET_EFAULT;
+        }
+        thunk_convert(argptr, host_ifconf, arg_type, THUNK_TARGET);
+        unlock_user(argptr, arg, target_size);
+
+        /* copy ifreq[] to target user */
+
+        argptr = lock_user(VERIFY_WRITE, target_ifc_buf, target_ifc_len, 0);
+        for (i = 0; i < nb_ifreq ; i++) {
+            thunk_convert(argptr + i * target_ifreq_size,
+                          host_ifc_buf + i * sizeof(struct ifreq),
+                          ifreq_arg_type, THUNK_TARGET);
+        }
+        unlock_user(argptr, target_ifc_buf, target_ifc_len);
+    }
+
+    if (free_buf) {
+        free(host_ifconf);
+    }
+
+    return ret;
+}
+
+#if defined(CONFIG_USBFS)
+#if HOST_LONG_BITS > 64
+#error USBDEVFS thunks do not support >64 bit hosts yet.
+#endif
+struct live_urb {
+    uint64_t target_urb_adr;
+    uint64_t target_buf_adr;
+    char *target_buf_ptr;
+    struct usbdevfs_urb host_urb;
+};
+
+static GHashTable *usbdevfs_urb_hashtable(void)
+{
+    static GHashTable *urb_hashtable;
+
+    if (!urb_hashtable) {
+        urb_hashtable = g_hash_table_new(g_int64_hash, g_int64_equal);
+    }
+    return urb_hashtable;
+}
+
+static void urb_hashtable_insert(struct live_urb *urb)
+{
+    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
+    g_hash_table_insert(urb_hashtable, urb, urb);
+}
+
+static struct live_urb *urb_hashtable_lookup(uint64_t target_urb_adr)
+{
+    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
+    return g_hash_table_lookup(urb_hashtable, &target_urb_adr);
+}
+
+static void urb_hashtable_remove(struct live_urb *urb)
+{
+    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
+    g_hash_table_remove(urb_hashtable, urb);
+}
+
+static abi_long
+do_ioctl_usbdevfs_reapurb(const IOCTLEntry *ie, uint8_t *buf_temp,
+                          int fd, int cmd, abi_long arg)
+{
+    const argtype usbfsurb_arg_type[] = { MK_STRUCT(STRUCT_usbdevfs_urb) };
+    const argtype ptrvoid_arg_type[] = { TYPE_PTRVOID, 0, 0 };
+    struct live_urb *lurb;
+    void *argptr;
+    uint64_t hurb;
+    int target_size;
+    uintptr_t target_urb_adr;
+    abi_long ret;
+
+    target_size = thunk_type_size(usbfsurb_arg_type, THUNK_TARGET);
+
+    memset(buf_temp, 0, sizeof(uint64_t));
+    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
+    if (is_error(ret)) {
+        return ret;
+    }
+
+    memcpy(&hurb, buf_temp, sizeof(uint64_t));
+    lurb = (void *)((uintptr_t)hurb - offsetof(struct live_urb, host_urb));
+    if (!lurb->target_urb_adr) {
+        return -TARGET_EFAULT;
+    }
+    urb_hashtable_remove(lurb);
+    unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr,
+                lurb->host_urb.buffer_length);
+    lurb->target_buf_ptr = NULL;
+
+    /* restore the guest buffer pointer */
+    lurb->host_urb.buffer = (void *)(uintptr_t)lurb->target_buf_adr;
+
+    /* update the guest urb struct */
+    argptr = lock_user(VERIFY_WRITE, lurb->target_urb_adr, target_size, 0);
+    if (!argptr) {
+        g_free(lurb);
+        return -TARGET_EFAULT;
+    }
+    thunk_convert(argptr, &lurb->host_urb, usbfsurb_arg_type, THUNK_TARGET);
+    unlock_user(argptr, lurb->target_urb_adr, target_size);
+
+    target_size = thunk_type_size(ptrvoid_arg_type, THUNK_TARGET);
+    /* write back the urb handle */
+    argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
+    if (!argptr) {
+        g_free(lurb);
+        return -TARGET_EFAULT;
+    }
+
+    /* GHashTable uses 64-bit keys but thunk_convert expects uintptr_t */
+    target_urb_adr = lurb->target_urb_adr;
+    thunk_convert(argptr, &target_urb_adr, ptrvoid_arg_type, THUNK_TARGET);
+    unlock_user(argptr, arg, target_size);
+
+    g_free(lurb);
+    return ret;
+}
+
+static abi_long
+do_ioctl_usbdevfs_discardurb(const IOCTLEntry *ie,
+                             uint8_t *buf_temp __attribute__((unused)),
+                             int fd, int cmd, abi_long arg)
+{
+    struct live_urb *lurb;
+
+    /* map target address back to host URB with metadata. */
+    lurb = urb_hashtable_lookup(arg);
+    if (!lurb) {
+        return -TARGET_EFAULT;
+    }
+    return get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));
+}
+
+static abi_long
+do_ioctl_usbdevfs_submiturb(const IOCTLEntry *ie, uint8_t *buf_temp,
+                            int fd, int cmd, abi_long arg)
+{
+    const argtype *arg_type = ie->arg_type;
+    int target_size;
+    abi_long ret;
+    void *argptr;
+    int rw_dir;
+    struct live_urb *lurb;
+
+    /*
+     * each submitted URB needs to map to a unique ID for the
+     * kernel, and that unique ID needs to be a pointer to
+     * host memory.  hence, we need to malloc for each URB.
+     * isochronous transfers have a variable length struct.
+     */
+    arg_type++;
+    target_size = thunk_type_size(arg_type, THUNK_TARGET);
+
+    /* construct host copy of urb and metadata */
+    lurb = g_try_malloc0(sizeof(struct live_urb));
+    if (!lurb) {
+        return -TARGET_ENOMEM;
+    }
+
+    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+    if (!argptr) {
+        g_free(lurb);
+        return -TARGET_EFAULT;
+    }
+    thunk_convert(&lurb->host_urb, argptr, arg_type, THUNK_HOST);
+    unlock_user(argptr, arg, 0);
+
+    lurb->target_urb_adr = arg;
+    lurb->target_buf_adr = (uintptr_t)lurb->host_urb.buffer;
+
+    /* buffer space used depends on endpoint type so lock the entire buffer */
+    /* control type urbs should check the buffer contents for true direction */
+    rw_dir = lurb->host_urb.endpoint & USB_DIR_IN ? VERIFY_WRITE : VERIFY_READ;
+    lurb->target_buf_ptr = lock_user(rw_dir, lurb->target_buf_adr,
+                                     lurb->host_urb.buffer_length, 1);
+    if (lurb->target_buf_ptr == NULL) {
+        g_free(lurb);
+        return -TARGET_EFAULT;
+    }
+
+    /* update buffer pointer in host copy */
+    lurb->host_urb.buffer = lurb->target_buf_ptr;
+
+    ret = get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));
+    if (is_error(ret)) {
+        unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr, 0);
+        g_free(lurb);
+    } else {
+        urb_hashtable_insert(lurb);
+    }
+
+    return ret;
+}
+#endif /* CONFIG_USBFS */
+
+static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
+                            int cmd, abi_long arg)
+{
+    void *argptr;
+    struct dm_ioctl *host_dm;
+    abi_long guest_data;
+    uint32_t guest_data_size;
+    int target_size;
+    const argtype *arg_type = ie->arg_type;
+    abi_long ret;
+    void *big_buf = NULL;
+    char *host_data;
+
+    arg_type++;
+    target_size = thunk_type_size(arg_type, 0);
+    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+    if (!argptr) {
+        ret = -TARGET_EFAULT;
+        goto out;
+    }
+    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+    unlock_user(argptr, arg, 0);
+
+    /* buf_temp is too small, so fetch things into a bigger buffer */
+    big_buf = g_malloc0(((struct dm_ioctl *)buf_temp)->data_size * 2);
+    memcpy(big_buf, buf_temp, target_size);
+    buf_temp = big_buf;
+    host_dm = big_buf;
+
+    guest_data = arg + host_dm->data_start;
+    if ((guest_data - arg) < 0) {
+        ret = -TARGET_EINVAL;
+        goto out;
+    }
+    guest_data_size = host_dm->data_size - host_dm->data_start;
+    host_data = (char *)host_dm + host_dm->data_start;
+
+    argptr = lock_user(VERIFY_READ, guest_data, guest_data_size, 1);
+    if (!argptr) {
+        ret = -TARGET_EFAULT;
+        goto out;
+    }
+
+    switch (ie->host_cmd) {
+    case DM_REMOVE_ALL:
+    case DM_LIST_DEVICES:
+    case DM_DEV_CREATE:
+    case DM_DEV_REMOVE:
+    case DM_DEV_SUSPEND:
+    case DM_DEV_STATUS:
+    case DM_DEV_WAIT:
+    case DM_TABLE_STATUS:
+    case DM_TABLE_CLEAR:
+    case DM_TABLE_DEPS:
+    case DM_LIST_VERSIONS:
+        /* no input data */
+        break;
+    case DM_DEV_RENAME:
+    case DM_DEV_SET_GEOMETRY:
+        /* data contains only strings */
+        memcpy(host_data, argptr, guest_data_size);
+        break;
+    case DM_TARGET_MSG:
+        memcpy(host_data, argptr, guest_data_size);
+        *(uint64_t *)host_data = tswap64(*(uint64_t *)argptr);
+        break;
+    case DM_TABLE_LOAD:
+    {
+        void *gspec = argptr;
+        void *cur_data = host_data;
+        const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };
+        int spec_size = thunk_type_size(arg_type, 0);
+        int i;
+
+        for (i = 0; i < host_dm->target_count; i++) {
+            struct dm_target_spec *spec = cur_data;
+            uint32_t next;
+            int slen;
+
+            thunk_convert(spec, gspec, arg_type, THUNK_HOST);
+            slen = strlen((char *)gspec + spec_size) + 1;
+            next = spec->next;
+            spec->next = sizeof(*spec) + slen;
+            strcpy((char *)&spec[1], gspec + spec_size);
+            gspec += next;
+            cur_data += spec->next;
+        }
+        break;
+    }
+    default:
+        ret = -TARGET_EINVAL;
+        unlock_user(argptr, guest_data, 0);
+        goto out;
+    }
+    unlock_user(argptr, guest_data, 0);
+
+    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
+    if (!is_error(ret)) {
+        guest_data = arg + host_dm->data_start;
+        guest_data_size = host_dm->data_size - host_dm->data_start;
+        argptr = lock_user(VERIFY_WRITE, guest_data, guest_data_size, 0);
+        switch (ie->host_cmd) {
+        case DM_REMOVE_ALL:
+        case DM_DEV_CREATE:
+        case DM_DEV_REMOVE:
+        case DM_DEV_RENAME:
+        case DM_DEV_SUSPEND:
+        case DM_DEV_STATUS:
+        case DM_TABLE_LOAD:
+        case DM_TABLE_CLEAR:
+        case DM_TARGET_MSG:
+        case DM_DEV_SET_GEOMETRY:
+            /* no return data */
+            break;
+        case DM_LIST_DEVICES:
+        {
+            struct dm_name_list *nl = (void *)host_dm + host_dm->data_start;
+            uint32_t remaining_data = guest_data_size;
+            void *cur_data = argptr;
+            const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) };
+            int nl_size = 12; /* can't use thunk_size due to alignment */
+
+            while (1) {
+                uint32_t next = nl->next;
+                if (next) {
+                    nl->next = nl_size + (strlen(nl->name) + 1);
+                }
+                if (remaining_data < nl->next) {
+                    host_dm->flags |= DM_BUFFER_FULL_FLAG;
+                    break;
+                }
+                thunk_convert(cur_data, nl, arg_type, THUNK_TARGET);
+                strcpy(cur_data + nl_size, nl->name);
+                cur_data += nl->next;
+                remaining_data -= nl->next;
+                if (!next) {
+                    break;
+                }
+                nl = (void *)nl + next;
+            }
+            break;
+        }
+        case DM_DEV_WAIT:
+        case DM_TABLE_STATUS:
+        {
+            struct dm_target_spec *spec
+                = (void *)host_dm + host_dm->data_start;
+            void *cur_data = argptr;
+            const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };
+            int spec_size = thunk_type_size(arg_type, 0);
+            int i;
+
+            for (i = 0; i < host_dm->target_count; i++) {
+                uint32_t next = spec->next;
+                int slen = strlen((char *)&spec[1]) + 1;
+                spec->next = (cur_data - argptr) + spec_size + slen;
+                if (guest_data_size < spec->next) {
+                    host_dm->flags |= DM_BUFFER_FULL_FLAG;
+                    break;
+                }
+                thunk_convert(cur_data, spec, arg_type, THUNK_TARGET);
+                strcpy(cur_data + spec_size, (char *)&spec[1]);
+                cur_data = argptr + spec->next;
+                spec = (void *)host_dm + host_dm->data_start + next;
+            }
+            break;
+        }
+        case DM_TABLE_DEPS:
+        {
+            void *hdata = (void *)host_dm + host_dm->data_start;
+            int count = *(uint32_t *)hdata;
+            uint64_t *hdev = hdata + 8;
+            uint64_t *gdev = argptr + 8;
+            int i;
+
+            *(uint32_t *)argptr = tswap32(count);
+            for (i = 0; i < count; i++) {
+                *gdev = tswap64(*hdev);
+                gdev++;
+                hdev++;
+            }
+            break;
+        }
+        case DM_LIST_VERSIONS:
+        {
+            struct dm_target_versions *vers
+                = (void *)host_dm + host_dm->data_start;
+            uint32_t remaining_data = guest_data_size;
+            void *cur_data = argptr;
+            const argtype arg_type[]
+                = { MK_STRUCT(STRUCT_dm_target_versions) };
+            int vers_size = thunk_type_size(arg_type, 0);
+
+            while (1) {
+                uint32_t next = vers->next;
+                if (next) {
+                    vers->next = vers_size + strlen(vers->name) + 1;
+                }
+                if (remaining_data < vers->next) {
+                    host_dm->flags |= DM_BUFFER_FULL_FLAG;
+                    break;
+                }
+                thunk_convert(cur_data, vers, arg_type, THUNK_TARGET);
+                strcpy(cur_data + vers_size, vers->name);
+                cur_data += vers->next;
+                remaining_data -= vers->next;
+                if (!next) {
+                    break;
+                }
+                vers = (void *)vers + next;
+            }
+            break;
+        }
+        default:
+            unlock_user(argptr, guest_data, 0);
+            ret = -TARGET_EINVAL;
+            goto out;
+        }
+        unlock_user(argptr, guest_data, guest_data_size);
+
+        argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
+        if (!argptr) {
+            ret = -TARGET_EFAULT;
+            goto out;
+        }
+        thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
+        unlock_user(argptr, arg, target_size);
+    }
+out:
+    g_free(big_buf);
+    return ret;
+}
+
+static abi_long do_ioctl_blkpg(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
+                               int cmd, abi_long arg)
+{
+    void *argptr;
+    int target_size;
+    const argtype *arg_type = ie->arg_type;
+    const argtype part_arg_type[] = { MK_STRUCT(STRUCT_blkpg_partition) };
+    abi_long ret;
+    struct blkpg_ioctl_arg *host_blkpg = (void *)buf_temp;
+    struct blkpg_partition host_part;
+
+    /* Read and convert blkpg */
+    arg_type++;
+    target_size = thunk_type_size(arg_type, 0);
+    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+    if (!argptr) {
+        ret = -TARGET_EFAULT;
+        goto out;
+    }
+    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+    unlock_user(argptr, arg, 0);
+
+    switch (host_blkpg->op) {
+    case BLKPG_ADD_PARTITION:
+    case BLKPG_DEL_PARTITION:
+        /* payload is struct blkpg_partition */
+        break;
+    default:
+        /* Unknown opcode */
+        ret = -TARGET_EINVAL;
+        goto out;
+    }
+
+    /* Read and convert blkpg->data */
+    arg = (abi_long)(uintptr_t)host_blkpg->data;
+    target_size = thunk_type_size(part_arg_type, 0);
+    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+    if (!argptr) {
+        ret = -TARGET_EFAULT;
+        goto out;
+    }
+    thunk_convert(&host_part, argptr, part_arg_type, THUNK_HOST);
+    unlock_user(argptr, arg, 0);
+
+    /* Swizzle the data pointer to our local copy and call! */
+    host_blkpg->data = &host_part;
+    ret = get_errno(safe_ioctl(fd, ie->host_cmd, host_blkpg));
+
+out:
+    return ret;
+}
+
+static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp,
+                            int fd, int cmd, abi_long arg)
+{
+    const argtype *arg_type = ie->arg_type;
+    const StructEntry *se;
+    const argtype *field_types;
+    const int *dst_offsets, *src_offsets;
+    int target_size;
+    void *argptr;
+    abi_ulong *target_rt_dev_ptr;
+    unsigned long *host_rt_dev_ptr;
+    abi_long ret;
+    int i;
+
+    assert(ie->access == IOC_W);
+    assert(*arg_type == TYPE_PTR);
+    arg_type++;
+    assert(*arg_type == TYPE_STRUCT);
+    target_size = thunk_type_size(arg_type, 0);
+    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+    if (!argptr) {
+        return -TARGET_EFAULT;
+    }
+    arg_type++;
+    assert(*arg_type == (int)STRUCT_rtentry);
+    se = struct_entries + *arg_type++;
+    assert(se->convert[0] == NULL);
+    /* convert struct here to be able to catch rt_dev string */
+    field_types = se->field_types;
+    dst_offsets = se->field_offsets[THUNK_HOST];
+    src_offsets = se->field_offsets[THUNK_TARGET];
+    for (i = 0; i < se->nb_fields; i++) {
+        if (dst_offsets[i] == offsetof(struct rtentry, rt_dev)) {
+            assert(*field_types == TYPE_PTRVOID);
+            target_rt_dev_ptr = (abi_ulong *)(argptr + src_offsets[i]);
+            host_rt_dev_ptr = (unsigned long *)(buf_temp + dst_offsets[i]);
+            if (*target_rt_dev_ptr != 0) {
+                *host_rt_dev_ptr = (unsigned long)lock_user_string(
+                    tswapal(*target_rt_dev_ptr));
+                if (!*host_rt_dev_ptr) {
+                    unlock_user(argptr, arg, 0);
+                    return -TARGET_EFAULT;
+                }
+            } else {
+                *host_rt_dev_ptr = 0;
+            }
+            field_types++;
+            continue;
+        }
+        field_types = thunk_convert(buf_temp + dst_offsets[i],
+                                    argptr + src_offsets[i],
+                                    field_types, THUNK_HOST);
+    }
+    unlock_user(argptr, arg, 0);
+
+    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
+    if (*host_rt_dev_ptr != 0) {
+        unlock_user((void *)*host_rt_dev_ptr,
+                    *target_rt_dev_ptr, 0);
+    }
+    return ret;
+}
+
+static abi_long do_ioctl_kdsigaccept(const IOCTLEntry *ie, uint8_t *buf_temp,
+                                     int fd, int cmd, abi_long arg)
+{
+    int sig = target_to_host_signal(arg);
+    return get_errno(safe_ioctl(fd, ie->host_cmd, sig));
+}
+
+#ifdef TIOCGPTPEER
+static abi_long do_ioctl_tiocgptpeer(const IOCTLEntry *ie, uint8_t *buf_temp,
+                                     int fd, int cmd, abi_long arg)
+{
+    int flags = target_to_host_bitmask(arg, fcntl_flags_tbl);
+    return get_errno(safe_ioctl(fd, ie->host_cmd, flags));
+}
+#endif
+
+static IOCTLEntry ioctl_entries[] = {
+#define IOCTL(cmd, access, ...)                                 \
+    { TARGET_ ## cmd, cmd, #cmd, access, 0, {  __VA_ARGS__ } },
+#define IOCTL_SPECIAL(cmd, access, dofn, ...)                           \
+    { TARGET_ ## cmd, cmd, #cmd, access, dofn, {  __VA_ARGS__ } },
+#define IOCTL_IGNORE(cmd)                       \
+    { TARGET_ ## cmd, 0, #cmd },
+#include "ioctls.h"
+    { 0, 0, },
+};
+
+/* ??? Implement proper locking for ioctls.  */
+SYSCALL_IMPL(ioctl)
+{
+    int fd = arg1;
+    abi_ulong cmd = arg2;
+    abi_ulong arg = arg3;
+    const IOCTLEntry *ie;
+    const argtype *arg_type;
+    abi_long ret;
+    uint8_t buf_temp[MAX_STRUCT_SIZE];
+    int target_size;
+    void *argptr;
+
+    for (ie = ioctl_entries; ; ie++) {
+        if (ie->target_cmd == 0) {
+            gemu_log("Unsupported ioctl: cmd=0x%04lx\n", (long)cmd);
+            return -TARGET_ENOSYS;
+        }
+        if (ie->target_cmd == cmd) {
+            break;
+        }
+    }
+
+    arg_type = ie->arg_type;
+    if (ie->do_ioctl) {
+        return ie->do_ioctl(ie, buf_temp, fd, cmd, arg);
+    } else if (!ie->host_cmd) {
+        /*
+         * Some architectures define BSD ioctls in their headers
+         * that are not implemented in Linux.
+         */
+        return -TARGET_ENOSYS;
+    }
+
+    switch (arg_type[0]) {
+    case TYPE_NULL:
+        /* no argument */
+        ret = get_errno(safe_ioctl(fd, ie->host_cmd));
+        break;
+    case TYPE_PTRVOID:
+    case TYPE_INT:
+        ret = get_errno(safe_ioctl(fd, ie->host_cmd, arg));
+        break;
+    case TYPE_PTR:
+        arg_type++;
+        target_size = thunk_type_size(arg_type, 0);
+        switch (ie->access) {
+        case IOC_R:
+            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
+            if (!is_error(ret)) {
+                argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
+                if (!argptr) {
+                    return -TARGET_EFAULT;
+                }
+                thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
+                unlock_user(argptr, arg, target_size);
+            }
+            break;
+        case IOC_W:
+            argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+            if (!argptr) {
+                return -TARGET_EFAULT;
+            }
+            thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+            unlock_user(argptr, arg, 0);
+            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
+            break;
+        default:
+        case IOC_RW:
+            argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+            if (!argptr) {
+                return -TARGET_EFAULT;
+            }
+            thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+            unlock_user(argptr, arg, 0);
+            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
+            if (!is_error(ret)) {
+                argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
+                if (!argptr) {
+                    return -TARGET_EFAULT;
+                }
+                thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
+                unlock_user(argptr, arg, target_size);
+            }
+            break;
+        }
+        break;
+    default:
+        gemu_log("Unsupported ioctl type: cmd=0x%04lx type=%d\n",
+                 (long)cmd, arg_type[0]);
+        ret = -TARGET_ENOSYS;
+        break;
+    }
+    return ret;
+}
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 82b7267b20..03ecf0a15a 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -2935,846 +2935,6 @@  STRUCT_MAX
 #undef STRUCT
 #undef STRUCT_SPECIAL
 
-typedef struct IOCTLEntry IOCTLEntry;
-
-typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp,
-                             int fd, int cmd, abi_long arg);
-
-struct IOCTLEntry {
-    int target_cmd;
-    unsigned int host_cmd;
-    const char *name;
-    int access;
-    do_ioctl_fn *do_ioctl;
-    const argtype arg_type[5];
-};
-
-#define IOC_R 0x0001
-#define IOC_W 0x0002
-#define IOC_RW (IOC_R | IOC_W)
-
-#define MAX_STRUCT_SIZE 4096
-
-#ifdef CONFIG_FIEMAP
-/* So fiemap access checks don't overflow on 32 bit systems.
- * This is very slightly smaller than the limit imposed by
- * the underlying kernel.
- */
-#define FIEMAP_MAX_EXTENTS ((UINT_MAX - sizeof(struct fiemap))  \
-                            / sizeof(struct fiemap_extent))
-
-static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp,
-                                       int fd, int cmd, abi_long arg)
-{
-    /* The parameter for this ioctl is a struct fiemap followed
-     * by an array of struct fiemap_extent whose size is set
-     * in fiemap->fm_extent_count. The array is filled in by the
-     * ioctl.
-     */
-    int target_size_in, target_size_out;
-    struct fiemap *fm;
-    const argtype *arg_type = ie->arg_type;
-    const argtype extent_arg_type[] = { MK_STRUCT(STRUCT_fiemap_extent) };
-    void *argptr, *p;
-    abi_long ret;
-    int i, extent_size = thunk_type_size(extent_arg_type, 0);
-    uint32_t outbufsz;
-    int free_fm = 0;
-
-    assert(arg_type[0] == TYPE_PTR);
-    assert(ie->access == IOC_RW);
-    arg_type++;
-    target_size_in = thunk_type_size(arg_type, 0);
-    argptr = lock_user(VERIFY_READ, arg, target_size_in, 1);
-    if (!argptr) {
-        return -TARGET_EFAULT;
-    }
-    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
-    unlock_user(argptr, arg, 0);
-    fm = (struct fiemap *)buf_temp;
-    if (fm->fm_extent_count > FIEMAP_MAX_EXTENTS) {
-        return -TARGET_EINVAL;
-    }
-
-    outbufsz = sizeof (*fm) +
-        (sizeof(struct fiemap_extent) * fm->fm_extent_count);
-
-    if (outbufsz > MAX_STRUCT_SIZE) {
-        /* We can't fit all the extents into the fixed size buffer.
-         * Allocate one that is large enough and use it instead.
-         */
-        fm = g_try_malloc(outbufsz);
-        if (!fm) {
-            return -TARGET_ENOMEM;
-        }
-        memcpy(fm, buf_temp, sizeof(struct fiemap));
-        free_fm = 1;
-    }
-    ret = get_errno(safe_ioctl(fd, ie->host_cmd, fm));
-    if (!is_error(ret)) {
-        target_size_out = target_size_in;
-        /* An extent_count of 0 means we were only counting the extents
-         * so there are no structs to copy
-         */
-        if (fm->fm_extent_count != 0) {
-            target_size_out += fm->fm_mapped_extents * extent_size;
-        }
-        argptr = lock_user(VERIFY_WRITE, arg, target_size_out, 0);
-        if (!argptr) {
-            ret = -TARGET_EFAULT;
-        } else {
-            /* Convert the struct fiemap */
-            thunk_convert(argptr, fm, arg_type, THUNK_TARGET);
-            if (fm->fm_extent_count != 0) {
-                p = argptr + target_size_in;
-                /* ...and then all the struct fiemap_extents */
-                for (i = 0; i < fm->fm_mapped_extents; i++) {
-                    thunk_convert(p, &fm->fm_extents[i], extent_arg_type,
-                                  THUNK_TARGET);
-                    p += extent_size;
-                }
-            }
-            unlock_user(argptr, arg, target_size_out);
-        }
-    }
-    if (free_fm) {
-        g_free(fm);
-    }
-    return ret;
-}
-#endif
-
-static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,
-                                int fd, int cmd, abi_long arg)
-{
-    const argtype *arg_type = ie->arg_type;
-    int target_size;
-    void *argptr;
-    int ret;
-    struct ifconf *host_ifconf;
-    uint32_t outbufsz;
-    const argtype ifreq_arg_type[] = { MK_STRUCT(STRUCT_sockaddr_ifreq) };
-    int target_ifreq_size;
-    int nb_ifreq;
-    int free_buf = 0;
-    int i;
-    int target_ifc_len;
-    abi_long target_ifc_buf;
-    int host_ifc_len;
-    char *host_ifc_buf;
-
-    assert(arg_type[0] == TYPE_PTR);
-    assert(ie->access == IOC_RW);
-
-    arg_type++;
-    target_size = thunk_type_size(arg_type, 0);
-
-    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
-    if (!argptr)
-        return -TARGET_EFAULT;
-    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
-    unlock_user(argptr, arg, 0);
-
-    host_ifconf = (struct ifconf *)(unsigned long)buf_temp;
-    target_ifc_len = host_ifconf->ifc_len;
-    target_ifc_buf = (abi_long)(unsigned long)host_ifconf->ifc_buf;
-
-    target_ifreq_size = thunk_type_size(ifreq_arg_type, 0);
-    nb_ifreq = target_ifc_len / target_ifreq_size;
-    host_ifc_len = nb_ifreq * sizeof(struct ifreq);
-
-    outbufsz = sizeof(*host_ifconf) + host_ifc_len;
-    if (outbufsz > MAX_STRUCT_SIZE) {
-        /* We can't fit all the extents into the fixed size buffer.
-         * Allocate one that is large enough and use it instead.
-         */
-        host_ifconf = malloc(outbufsz);
-        if (!host_ifconf) {
-            return -TARGET_ENOMEM;
-        }
-        memcpy(host_ifconf, buf_temp, sizeof(*host_ifconf));
-        free_buf = 1;
-    }
-    host_ifc_buf = (char*)host_ifconf + sizeof(*host_ifconf);
-
-    host_ifconf->ifc_len = host_ifc_len;
-    host_ifconf->ifc_buf = host_ifc_buf;
-
-    ret = get_errno(safe_ioctl(fd, ie->host_cmd, host_ifconf));
-    if (!is_error(ret)) {
-	/* convert host ifc_len to target ifc_len */
-
-        nb_ifreq = host_ifconf->ifc_len / sizeof(struct ifreq);
-        target_ifc_len = nb_ifreq * target_ifreq_size;
-        host_ifconf->ifc_len = target_ifc_len;
-
-	/* restore target ifc_buf */
-
-        host_ifconf->ifc_buf = (char *)(unsigned long)target_ifc_buf;
-
-	/* copy struct ifconf to target user */
-
-        argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
-        if (!argptr)
-            return -TARGET_EFAULT;
-        thunk_convert(argptr, host_ifconf, arg_type, THUNK_TARGET);
-        unlock_user(argptr, arg, target_size);
-
-	/* copy ifreq[] to target user */
-
-        argptr = lock_user(VERIFY_WRITE, target_ifc_buf, target_ifc_len, 0);
-        for (i = 0; i < nb_ifreq ; i++) {
-            thunk_convert(argptr + i * target_ifreq_size,
-                          host_ifc_buf + i * sizeof(struct ifreq),
-                          ifreq_arg_type, THUNK_TARGET);
-        }
-        unlock_user(argptr, target_ifc_buf, target_ifc_len);
-    }
-
-    if (free_buf) {
-        free(host_ifconf);
-    }
-
-    return ret;
-}
-
-#if defined(CONFIG_USBFS)
-#if HOST_LONG_BITS > 64
-#error USBDEVFS thunks do not support >64 bit hosts yet.
-#endif
-struct live_urb {
-    uint64_t target_urb_adr;
-    uint64_t target_buf_adr;
-    char *target_buf_ptr;
-    struct usbdevfs_urb host_urb;
-};
-
-static GHashTable *usbdevfs_urb_hashtable(void)
-{
-    static GHashTable *urb_hashtable;
-
-    if (!urb_hashtable) {
-        urb_hashtable = g_hash_table_new(g_int64_hash, g_int64_equal);
-    }
-    return urb_hashtable;
-}
-
-static void urb_hashtable_insert(struct live_urb *urb)
-{
-    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
-    g_hash_table_insert(urb_hashtable, urb, urb);
-}
-
-static struct live_urb *urb_hashtable_lookup(uint64_t target_urb_adr)
-{
-    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
-    return g_hash_table_lookup(urb_hashtable, &target_urb_adr);
-}
-
-static void urb_hashtable_remove(struct live_urb *urb)
-{
-    GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
-    g_hash_table_remove(urb_hashtable, urb);
-}
-
-static abi_long
-do_ioctl_usbdevfs_reapurb(const IOCTLEntry *ie, uint8_t *buf_temp,
-                          int fd, int cmd, abi_long arg)
-{
-    const argtype usbfsurb_arg_type[] = { MK_STRUCT(STRUCT_usbdevfs_urb) };
-    const argtype ptrvoid_arg_type[] = { TYPE_PTRVOID, 0, 0 };
-    struct live_urb *lurb;
-    void *argptr;
-    uint64_t hurb;
-    int target_size;
-    uintptr_t target_urb_adr;
-    abi_long ret;
-
-    target_size = thunk_type_size(usbfsurb_arg_type, THUNK_TARGET);
-
-    memset(buf_temp, 0, sizeof(uint64_t));
-    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
-    if (is_error(ret)) {
-        return ret;
-    }
-
-    memcpy(&hurb, buf_temp, sizeof(uint64_t));
-    lurb = (void *)((uintptr_t)hurb - offsetof(struct live_urb, host_urb));
-    if (!lurb->target_urb_adr) {
-        return -TARGET_EFAULT;
-    }
-    urb_hashtable_remove(lurb);
-    unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr,
-        lurb->host_urb.buffer_length);
-    lurb->target_buf_ptr = NULL;
-
-    /* restore the guest buffer pointer */
-    lurb->host_urb.buffer = (void *)(uintptr_t)lurb->target_buf_adr;
-
-    /* update the guest urb struct */
-    argptr = lock_user(VERIFY_WRITE, lurb->target_urb_adr, target_size, 0);
-    if (!argptr) {
-        g_free(lurb);
-        return -TARGET_EFAULT;
-    }
-    thunk_convert(argptr, &lurb->host_urb, usbfsurb_arg_type, THUNK_TARGET);
-    unlock_user(argptr, lurb->target_urb_adr, target_size);
-
-    target_size = thunk_type_size(ptrvoid_arg_type, THUNK_TARGET);
-    /* write back the urb handle */
-    argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
-    if (!argptr) {
-        g_free(lurb);
-        return -TARGET_EFAULT;
-    }
-
-    /* GHashTable uses 64-bit keys but thunk_convert expects uintptr_t */
-    target_urb_adr = lurb->target_urb_adr;
-    thunk_convert(argptr, &target_urb_adr, ptrvoid_arg_type, THUNK_TARGET);
-    unlock_user(argptr, arg, target_size);
-
-    g_free(lurb);
-    return ret;
-}
-
-static abi_long
-do_ioctl_usbdevfs_discardurb(const IOCTLEntry *ie,
-                             uint8_t *buf_temp __attribute__((unused)),
-                             int fd, int cmd, abi_long arg)
-{
-    struct live_urb *lurb;
-
-    /* map target address back to host URB with metadata. */
-    lurb = urb_hashtable_lookup(arg);
-    if (!lurb) {
-        return -TARGET_EFAULT;
-    }
-    return get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));
-}
-
-static abi_long
-do_ioctl_usbdevfs_submiturb(const IOCTLEntry *ie, uint8_t *buf_temp,
-                            int fd, int cmd, abi_long arg)
-{
-    const argtype *arg_type = ie->arg_type;
-    int target_size;
-    abi_long ret;
-    void *argptr;
-    int rw_dir;
-    struct live_urb *lurb;
-
-    /*
-     * each submitted URB needs to map to a unique ID for the
-     * kernel, and that unique ID needs to be a pointer to
-     * host memory.  hence, we need to malloc for each URB.
-     * isochronous transfers have a variable length struct.
-     */
-    arg_type++;
-    target_size = thunk_type_size(arg_type, THUNK_TARGET);
-
-    /* construct host copy of urb and metadata */
-    lurb = g_try_malloc0(sizeof(struct live_urb));
-    if (!lurb) {
-        return -TARGET_ENOMEM;
-    }
-
-    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
-    if (!argptr) {
-        g_free(lurb);
-        return -TARGET_EFAULT;
-    }
-    thunk_convert(&lurb->host_urb, argptr, arg_type, THUNK_HOST);
-    unlock_user(argptr, arg, 0);
-
-    lurb->target_urb_adr = arg;
-    lurb->target_buf_adr = (uintptr_t)lurb->host_urb.buffer;
-
-    /* buffer space used depends on endpoint type so lock the entire buffer */
-    /* control type urbs should check the buffer contents for true direction */
-    rw_dir = lurb->host_urb.endpoint & USB_DIR_IN ? VERIFY_WRITE : VERIFY_READ;
-    lurb->target_buf_ptr = lock_user(rw_dir, lurb->target_buf_adr,
-        lurb->host_urb.buffer_length, 1);
-    if (lurb->target_buf_ptr == NULL) {
-        g_free(lurb);
-        return -TARGET_EFAULT;
-    }
-
-    /* update buffer pointer in host copy */
-    lurb->host_urb.buffer = lurb->target_buf_ptr;
-
-    ret = get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));
-    if (is_error(ret)) {
-        unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr, 0);
-        g_free(lurb);
-    } else {
-        urb_hashtable_insert(lurb);
-    }
-
-    return ret;
-}
-#endif /* CONFIG_USBFS */
-
-static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
-                            int cmd, abi_long arg)
-{
-    void *argptr;
-    struct dm_ioctl *host_dm;
-    abi_long guest_data;
-    uint32_t guest_data_size;
-    int target_size;
-    const argtype *arg_type = ie->arg_type;
-    abi_long ret;
-    void *big_buf = NULL;
-    char *host_data;
-
-    arg_type++;
-    target_size = thunk_type_size(arg_type, 0);
-    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
-    if (!argptr) {
-        ret = -TARGET_EFAULT;
-        goto out;
-    }
-    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
-    unlock_user(argptr, arg, 0);
-
-    /* buf_temp is too small, so fetch things into a bigger buffer */
-    big_buf = g_malloc0(((struct dm_ioctl*)buf_temp)->data_size * 2);
-    memcpy(big_buf, buf_temp, target_size);
-    buf_temp = big_buf;
-    host_dm = big_buf;
-
-    guest_data = arg + host_dm->data_start;
-    if ((guest_data - arg) < 0) {
-        ret = -TARGET_EINVAL;
-        goto out;
-    }
-    guest_data_size = host_dm->data_size - host_dm->data_start;
-    host_data = (char*)host_dm + host_dm->data_start;
-
-    argptr = lock_user(VERIFY_READ, guest_data, guest_data_size, 1);
-    if (!argptr) {
-        ret = -TARGET_EFAULT;
-        goto out;
-    }
-
-    switch (ie->host_cmd) {
-    case DM_REMOVE_ALL:
-    case DM_LIST_DEVICES:
-    case DM_DEV_CREATE:
-    case DM_DEV_REMOVE:
-    case DM_DEV_SUSPEND:
-    case DM_DEV_STATUS:
-    case DM_DEV_WAIT:
-    case DM_TABLE_STATUS:
-    case DM_TABLE_CLEAR:
-    case DM_TABLE_DEPS:
-    case DM_LIST_VERSIONS:
-        /* no input data */
-        break;
-    case DM_DEV_RENAME:
-    case DM_DEV_SET_GEOMETRY:
-        /* data contains only strings */
-        memcpy(host_data, argptr, guest_data_size);
-        break;
-    case DM_TARGET_MSG:
-        memcpy(host_data, argptr, guest_data_size);
-        *(uint64_t*)host_data = tswap64(*(uint64_t*)argptr);
-        break;
-    case DM_TABLE_LOAD:
-    {
-        void *gspec = argptr;
-        void *cur_data = host_data;
-        const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };
-        int spec_size = thunk_type_size(arg_type, 0);
-        int i;
-
-        for (i = 0; i < host_dm->target_count; i++) {
-            struct dm_target_spec *spec = cur_data;
-            uint32_t next;
-            int slen;
-
-            thunk_convert(spec, gspec, arg_type, THUNK_HOST);
-            slen = strlen((char*)gspec + spec_size) + 1;
-            next = spec->next;
-            spec->next = sizeof(*spec) + slen;
-            strcpy((char*)&spec[1], gspec + spec_size);
-            gspec += next;
-            cur_data += spec->next;
-        }
-        break;
-    }
-    default:
-        ret = -TARGET_EINVAL;
-        unlock_user(argptr, guest_data, 0);
-        goto out;
-    }
-    unlock_user(argptr, guest_data, 0);
-
-    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
-    if (!is_error(ret)) {
-        guest_data = arg + host_dm->data_start;
-        guest_data_size = host_dm->data_size - host_dm->data_start;
-        argptr = lock_user(VERIFY_WRITE, guest_data, guest_data_size, 0);
-        switch (ie->host_cmd) {
-        case DM_REMOVE_ALL:
-        case DM_DEV_CREATE:
-        case DM_DEV_REMOVE:
-        case DM_DEV_RENAME:
-        case DM_DEV_SUSPEND:
-        case DM_DEV_STATUS:
-        case DM_TABLE_LOAD:
-        case DM_TABLE_CLEAR:
-        case DM_TARGET_MSG:
-        case DM_DEV_SET_GEOMETRY:
-            /* no return data */
-            break;
-        case DM_LIST_DEVICES:
-        {
-            struct dm_name_list *nl = (void*)host_dm + host_dm->data_start;
-            uint32_t remaining_data = guest_data_size;
-            void *cur_data = argptr;
-            const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_name_list) };
-            int nl_size = 12; /* can't use thunk_size due to alignment */
-
-            while (1) {
-                uint32_t next = nl->next;
-                if (next) {
-                    nl->next = nl_size + (strlen(nl->name) + 1);
-                }
-                if (remaining_data < nl->next) {
-                    host_dm->flags |= DM_BUFFER_FULL_FLAG;
-                    break;
-                }
-                thunk_convert(cur_data, nl, arg_type, THUNK_TARGET);
-                strcpy(cur_data + nl_size, nl->name);
-                cur_data += nl->next;
-                remaining_data -= nl->next;
-                if (!next) {
-                    break;
-                }
-                nl = (void*)nl + next;
-            }
-            break;
-        }
-        case DM_DEV_WAIT:
-        case DM_TABLE_STATUS:
-        {
-            struct dm_target_spec *spec = (void*)host_dm + host_dm->data_start;
-            void *cur_data = argptr;
-            const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_spec) };
-            int spec_size = thunk_type_size(arg_type, 0);
-            int i;
-
-            for (i = 0; i < host_dm->target_count; i++) {
-                uint32_t next = spec->next;
-                int slen = strlen((char*)&spec[1]) + 1;
-                spec->next = (cur_data - argptr) + spec_size + slen;
-                if (guest_data_size < spec->next) {
-                    host_dm->flags |= DM_BUFFER_FULL_FLAG;
-                    break;
-                }
-                thunk_convert(cur_data, spec, arg_type, THUNK_TARGET);
-                strcpy(cur_data + spec_size, (char*)&spec[1]);
-                cur_data = argptr + spec->next;
-                spec = (void*)host_dm + host_dm->data_start + next;
-            }
-            break;
-        }
-        case DM_TABLE_DEPS:
-        {
-            void *hdata = (void*)host_dm + host_dm->data_start;
-            int count = *(uint32_t*)hdata;
-            uint64_t *hdev = hdata + 8;
-            uint64_t *gdev = argptr + 8;
-            int i;
-
-            *(uint32_t*)argptr = tswap32(count);
-            for (i = 0; i < count; i++) {
-                *gdev = tswap64(*hdev);
-                gdev++;
-                hdev++;
-            }
-            break;
-        }
-        case DM_LIST_VERSIONS:
-        {
-            struct dm_target_versions *vers = (void*)host_dm + host_dm->data_start;
-            uint32_t remaining_data = guest_data_size;
-            void *cur_data = argptr;
-            const argtype arg_type[] = { MK_STRUCT(STRUCT_dm_target_versions) };
-            int vers_size = thunk_type_size(arg_type, 0);
-
-            while (1) {
-                uint32_t next = vers->next;
-                if (next) {
-                    vers->next = vers_size + (strlen(vers->name) + 1);
-                }
-                if (remaining_data < vers->next) {
-                    host_dm->flags |= DM_BUFFER_FULL_FLAG;
-                    break;
-                }
-                thunk_convert(cur_data, vers, arg_type, THUNK_TARGET);
-                strcpy(cur_data + vers_size, vers->name);
-                cur_data += vers->next;
-                remaining_data -= vers->next;
-                if (!next) {
-                    break;
-                }
-                vers = (void*)vers + next;
-            }
-            break;
-        }
-        default:
-            unlock_user(argptr, guest_data, 0);
-            ret = -TARGET_EINVAL;
-            goto out;
-        }
-        unlock_user(argptr, guest_data, guest_data_size);
-
-        argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
-        if (!argptr) {
-            ret = -TARGET_EFAULT;
-            goto out;
-        }
-        thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
-        unlock_user(argptr, arg, target_size);
-    }
-out:
-    g_free(big_buf);
-    return ret;
-}
-
-static abi_long do_ioctl_blkpg(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
-                               int cmd, abi_long arg)
-{
-    void *argptr;
-    int target_size;
-    const argtype *arg_type = ie->arg_type;
-    const argtype part_arg_type[] = { MK_STRUCT(STRUCT_blkpg_partition) };
-    abi_long ret;
-
-    struct blkpg_ioctl_arg *host_blkpg = (void*)buf_temp;
-    struct blkpg_partition host_part;
-
-    /* Read and convert blkpg */
-    arg_type++;
-    target_size = thunk_type_size(arg_type, 0);
-    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
-    if (!argptr) {
-        ret = -TARGET_EFAULT;
-        goto out;
-    }
-    thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
-    unlock_user(argptr, arg, 0);
-
-    switch (host_blkpg->op) {
-    case BLKPG_ADD_PARTITION:
-    case BLKPG_DEL_PARTITION:
-        /* payload is struct blkpg_partition */
-        break;
-    default:
-        /* Unknown opcode */
-        ret = -TARGET_EINVAL;
-        goto out;
-    }
-
-    /* Read and convert blkpg->data */
-    arg = (abi_long)(uintptr_t)host_blkpg->data;
-    target_size = thunk_type_size(part_arg_type, 0);
-    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
-    if (!argptr) {
-        ret = -TARGET_EFAULT;
-        goto out;
-    }
-    thunk_convert(&host_part, argptr, part_arg_type, THUNK_HOST);
-    unlock_user(argptr, arg, 0);
-
-    /* Swizzle the data pointer to our local copy and call! */
-    host_blkpg->data = &host_part;
-    ret = get_errno(safe_ioctl(fd, ie->host_cmd, host_blkpg));
-
-out:
-    return ret;
-}
-
-static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp,
-                                int fd, int cmd, abi_long arg)
-{
-    const argtype *arg_type = ie->arg_type;
-    const StructEntry *se;
-    const argtype *field_types;
-    const int *dst_offsets, *src_offsets;
-    int target_size;
-    void *argptr;
-    abi_ulong *target_rt_dev_ptr;
-    unsigned long *host_rt_dev_ptr;
-    abi_long ret;
-    int i;
-
-    assert(ie->access == IOC_W);
-    assert(*arg_type == TYPE_PTR);
-    arg_type++;
-    assert(*arg_type == TYPE_STRUCT);
-    target_size = thunk_type_size(arg_type, 0);
-    argptr = lock_user(VERIFY_READ, arg, target_size, 1);
-    if (!argptr) {
-        return -TARGET_EFAULT;
-    }
-    arg_type++;
-    assert(*arg_type == (int)STRUCT_rtentry);
-    se = struct_entries + *arg_type++;
-    assert(se->convert[0] == NULL);
-    /* convert struct here to be able to catch rt_dev string */
-    field_types = se->field_types;
-    dst_offsets = se->field_offsets[THUNK_HOST];
-    src_offsets = se->field_offsets[THUNK_TARGET];
-    for (i = 0; i < se->nb_fields; i++) {
-        if (dst_offsets[i] == offsetof(struct rtentry, rt_dev)) {
-            assert(*field_types == TYPE_PTRVOID);
-            target_rt_dev_ptr = (abi_ulong *)(argptr + src_offsets[i]);
-            host_rt_dev_ptr = (unsigned long *)(buf_temp + dst_offsets[i]);
-            if (*target_rt_dev_ptr != 0) {
-                *host_rt_dev_ptr = (unsigned long)lock_user_string(
-                                                  tswapal(*target_rt_dev_ptr));
-                if (!*host_rt_dev_ptr) {
-                    unlock_user(argptr, arg, 0);
-                    return -TARGET_EFAULT;
-                }
-            } else {
-                *host_rt_dev_ptr = 0;
-            }
-            field_types++;
-            continue;
-        }
-        field_types = thunk_convert(buf_temp + dst_offsets[i],
-                                    argptr + src_offsets[i],
-                                    field_types, THUNK_HOST);
-    }
-    unlock_user(argptr, arg, 0);
-
-    ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
-    if (*host_rt_dev_ptr != 0) {
-        unlock_user((void *)*host_rt_dev_ptr,
-                    *target_rt_dev_ptr, 0);
-    }
-    return ret;
-}
-
-static abi_long do_ioctl_kdsigaccept(const IOCTLEntry *ie, uint8_t *buf_temp,
-                                     int fd, int cmd, abi_long arg)
-{
-    int sig = target_to_host_signal(arg);
-    return get_errno(safe_ioctl(fd, ie->host_cmd, sig));
-}
-
-#ifdef TIOCGPTPEER
-static abi_long do_ioctl_tiocgptpeer(const IOCTLEntry *ie, uint8_t *buf_temp,
-                                     int fd, int cmd, abi_long arg)
-{
-    int flags = target_to_host_bitmask(arg, fcntl_flags_tbl);
-    return get_errno(safe_ioctl(fd, ie->host_cmd, flags));
-}
-#endif
-
-static IOCTLEntry ioctl_entries[] = {
-#define IOCTL(cmd, access, ...) \
-    { TARGET_ ## cmd, cmd, #cmd, access, 0, {  __VA_ARGS__ } },
-#define IOCTL_SPECIAL(cmd, access, dofn, ...)                      \
-    { TARGET_ ## cmd, cmd, #cmd, access, dofn, {  __VA_ARGS__ } },
-#define IOCTL_IGNORE(cmd) \
-    { TARGET_ ## cmd, 0, #cmd },
-#include "ioctls.h"
-    { 0, 0, },
-};
-
-/* ??? Implement proper locking for ioctls.  */
-/* do_ioctl() Must return target values and target errnos. */
-static abi_long do_ioctl(int fd, int cmd, abi_long arg)
-{
-    const IOCTLEntry *ie;
-    const argtype *arg_type;
-    abi_long ret;
-    uint8_t buf_temp[MAX_STRUCT_SIZE];
-    int target_size;
-    void *argptr;
-
-    ie = ioctl_entries;
-    for(;;) {
-        if (ie->target_cmd == 0) {
-            gemu_log("Unsupported ioctl: cmd=0x%04lx\n", (long)cmd);
-            return -TARGET_ENOSYS;
-        }
-        if (ie->target_cmd == cmd)
-            break;
-        ie++;
-    }
-    arg_type = ie->arg_type;
-    if (ie->do_ioctl) {
-        return ie->do_ioctl(ie, buf_temp, fd, cmd, arg);
-    } else if (!ie->host_cmd) {
-        /* Some architectures define BSD ioctls in their headers
-           that are not implemented in Linux.  */
-        return -TARGET_ENOSYS;
-    }
-
-    switch(arg_type[0]) {
-    case TYPE_NULL:
-        /* no argument */
-        ret = get_errno(safe_ioctl(fd, ie->host_cmd));
-        break;
-    case TYPE_PTRVOID:
-    case TYPE_INT:
-        ret = get_errno(safe_ioctl(fd, ie->host_cmd, arg));
-        break;
-    case TYPE_PTR:
-        arg_type++;
-        target_size = thunk_type_size(arg_type, 0);
-        switch(ie->access) {
-        case IOC_R:
-            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
-            if (!is_error(ret)) {
-                argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
-                if (!argptr)
-                    return -TARGET_EFAULT;
-                thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
-                unlock_user(argptr, arg, target_size);
-            }
-            break;
-        case IOC_W:
-            argptr = lock_user(VERIFY_READ, arg, target_size, 1);
-            if (!argptr)
-                return -TARGET_EFAULT;
-            thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
-            unlock_user(argptr, arg, 0);
-            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
-            break;
-        default:
-        case IOC_RW:
-            argptr = lock_user(VERIFY_READ, arg, target_size, 1);
-            if (!argptr)
-                return -TARGET_EFAULT;
-            thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
-            unlock_user(argptr, arg, 0);
-            ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
-            if (!is_error(ret)) {
-                argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
-                if (!argptr)
-                    return -TARGET_EFAULT;
-                thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
-                unlock_user(argptr, arg, target_size);
-            }
-            break;
-        }
-        break;
-    default:
-        gemu_log("Unsupported ioctl type: cmd=0x%04lx type=%d\n",
-                 (long)cmd, arg_type[0]);
-        ret = -TARGET_ENOSYS;
-        break;
-    }
-    return ret;
-}
-
 static const bitmask_transtbl iflag_tbl[] = {
         { TARGET_IGNBRK, TARGET_IGNBRK, IGNBRK, IGNBRK },
         { TARGET_BRKINT, TARGET_BRKINT, BRKINT, BRKINT },
@@ -5241,8 +4401,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
     void *p;
 
     switch(num) {
-    case TARGET_NR_ioctl:
-        return do_ioctl(arg1, arg2, arg3);
 #ifdef TARGET_NR_fcntl
     case TARGET_NR_fcntl:
         return do_fcntl(arg1, arg2, arg3);
@@ -8811,6 +7969,7 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
                                 int64_t arg5, int64_t arg6)
 
 #include "syscall-file.inc.c"
+#include "syscall-ioctl.inc.c"
 #include "syscall-ipc.inc.c"
 #include "syscall-mem.inc.c"
 #include "syscall-proc.inc.c"
diff --git a/linux-user/strace.list b/linux-user/strace.list
index 9f2f8977b4..15208b5349 100644
--- a/linux-user/strace.list
+++ b/linux-user/strace.list
@@ -365,9 +365,6 @@ 
 #ifdef TARGET_NR_io_cancel
 { TARGET_NR_io_cancel, "io_cancel" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_ioctl
-{ TARGET_NR_ioctl, "ioctl" , NULL, NULL, NULL },
-#endif
 #ifdef TARGET_NR_io_destroy
 { TARGET_NR_io_destroy, "io_destroy" , NULL, NULL, NULL },
 #endif