[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v6 48/49] linux-user: Split out ioctl
From: |
Laurent Vivier |
Subject: |
Re: [Qemu-devel] [PATCH v6 48/49] linux-user: Split out ioctl |
Date: |
Wed, 13 Feb 2019 14:09:35 +0100 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.5.0 |
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 <address@hidden>
> ---
> 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
>
- Re: [Qemu-devel] [PATCH v6 48/49] linux-user: Split out ioctl,
Laurent Vivier <=