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.hender...@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 >