diff options
Diffstat (limited to 'linux-user')
| -rw-r--r-- | linux-user/ioctls.h | 46 | ||||
| -rw-r--r-- | linux-user/qemu.h | 74 | ||||
| -rw-r--r-- | linux-user/sparc/signal.c | 4 | ||||
| -rw-r--r-- | linux-user/syscall.c | 180 | ||||
| -rw-r--r-- | linux-user/syscall_defs.h | 28 | ||||
| -rw-r--r-- | linux-user/syscall_types.h | 68 |
6 files changed, 375 insertions, 25 deletions
diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 586c794639..ae8951625f 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -131,6 +131,52 @@ IOCTL(FS_IOC_GETFLAGS, IOC_R, MK_PTR(TYPE_INT)) IOCTL(FS_IOC_SETFLAGS, IOC_W, MK_PTR(TYPE_INT)) +#ifdef CONFIG_USBFS + /* USB ioctls */ + IOCTL(USBDEVFS_CONTROL, IOC_RW, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_ctrltransfer))) + IOCTL(USBDEVFS_BULK, IOC_RW, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_bulktransfer))) + IOCTL(USBDEVFS_RESETEP, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_SETINTERFACE, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_setinterface))) + IOCTL(USBDEVFS_SETCONFIGURATION, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_GETDRIVER, IOC_R, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_getdriver))) + IOCTL_SPECIAL(USBDEVFS_SUBMITURB, IOC_W, do_ioctl_usbdevfs_submiturb, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb))) + IOCTL_SPECIAL(USBDEVFS_DISCARDURB, IOC_RW, do_ioctl_usbdevfs_discardurb, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb))) + IOCTL_SPECIAL(USBDEVFS_REAPURB, IOC_R, do_ioctl_usbdevfs_reapurb, + MK_PTR(TYPE_PTRVOID)) + IOCTL_SPECIAL(USBDEVFS_REAPURBNDELAY, IOC_R, do_ioctl_usbdevfs_reapurb, + MK_PTR(TYPE_PTRVOID)) + IOCTL(USBDEVFS_DISCSIGNAL, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnectsignal))) + IOCTL(USBDEVFS_CLAIMINTERFACE, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_RELEASEINTERFACE, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_CONNECTINFO, IOC_R, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_connectinfo))) + IOCTL(USBDEVFS_IOCTL, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_usbdevfs_ioctl))) + IOCTL(USBDEVFS_HUB_PORTINFO, IOC_R, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_hub_portinfo))) + IOCTL(USBDEVFS_RESET, 0, TYPE_NULL) + IOCTL(USBDEVFS_CLEAR_HALT, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_DISCONNECT, 0, TYPE_NULL) + IOCTL(USBDEVFS_CONNECT, 0, TYPE_NULL) + IOCTL(USBDEVFS_CLAIM_PORT, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_RELEASE_PORT, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_GET_CAPABILITIES, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_DISCONNECT_CLAIM, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnect_claim))) +#ifdef USBDEVFS_DROP_PRIVILEGES + IOCTL(USBDEVFS_DROP_PRIVILEGES, IOC_W, MK_PTR(TYPE_INT)) +#endif +#ifdef USBDEVFS_GET_SPEED + IOCTL(USBDEVFS_GET_SPEED, 0, TYPE_NULL) +#endif +#endif /* CONFIG_USBFS */ + IOCTL(SIOCATMARK, IOC_R, MK_PTR(TYPE_INT)) IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT)) IOCTL(SIOCGIFFLAGS, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq))) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index b4959e41c6..1beb6a2cfc 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -461,27 +461,59 @@ static inline int access_ok(int type, abi_ulong addr, abi_ulong size) These are usually used to access struct data members once the struct has been locked - usually with lock_user_struct. */ -/* Tricky points: - - Use __builtin_choose_expr to avoid type promotion from ?:, - - Invalid sizes result in a compile time error stemming from - the fact that abort has no parameters. - - It's easier to use the endian-specific unaligned load/store - functions than host-endian unaligned load/store plus tswapN. */ - -#define __put_user_e(x, hptr, e) \ - (__builtin_choose_expr(sizeof(*(hptr)) == 1, stb_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 2, stw_##e##_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 4, stl_##e##_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 8, stq_##e##_p, abort)))) \ - ((hptr), (x)), (void)0) - -#define __get_user_e(x, hptr, e) \ - ((x) = (typeof(*hptr))( \ - __builtin_choose_expr(sizeof(*(hptr)) == 1, ldub_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 2, lduw_##e##_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 4, ldl_##e##_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 8, ldq_##e##_p, abort)))) \ - (hptr)), (void)0) +/* + * Tricky points: + * - Use __builtin_choose_expr to avoid type promotion from ?:, + * - Invalid sizes result in a compile time error stemming from + * the fact that abort has no parameters. + * - It's easier to use the endian-specific unaligned load/store + * functions than host-endian unaligned load/store plus tswapN. + * - The pragmas are necessary only to silence a clang false-positive + * warning: see https://bugs.llvm.org/show_bug.cgi?id=39113 . + * - We have to disable -Wpragmas warnings to avoid a complaint about + * an unknown warning type from older compilers that don't know about + * -Waddress-of-packed-member. + * - gcc has bugs in its _Pragma() support in some versions, eg + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83256 -- so we only + * include the warning-suppression pragmas for clang + */ +#ifdef __clang__ +#define PRAGMA_DISABLE_PACKED_WARNING \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wpragmas\""); \ + _Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"") + +#define PRAGMA_REENABLE_PACKED_WARNING \ + _Pragma("GCC diagnostic pop") + +#else +#define PRAGMA_DISABLE_PACKED_WARNING +#define PRAGMA_REENABLE_PACKED_WARNING +#endif + +#define __put_user_e(x, hptr, e) \ + do { \ + PRAGMA_DISABLE_PACKED_WARNING; \ + (__builtin_choose_expr(sizeof(*(hptr)) == 1, stb_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 2, stw_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 4, stl_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 8, stq_##e##_p, abort)))) \ + ((hptr), (x)), (void)0); \ + PRAGMA_REENABLE_PACKED_WARNING; \ + } while (0) + +#define __get_user_e(x, hptr, e) \ + do { \ + PRAGMA_DISABLE_PACKED_WARNING; \ + ((x) = (typeof(*hptr))( \ + __builtin_choose_expr(sizeof(*(hptr)) == 1, ldub_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 2, lduw_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 4, ldl_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 8, ldq_##e##_p, abort)))) \ + (hptr)), (void)0); \ + PRAGMA_REENABLE_PACKED_WARNING; \ + } while (0) + #ifdef TARGET_WORDS_BIGENDIAN # define __put_user(x, hptr) __put_user_e(x, hptr, be) diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index b4c60aa446..e44e99993c 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -258,10 +258,6 @@ void setup_frame(int sig, struct target_sigaction *ka, __put_user(val32, &sf->insns[1]); if (err) goto sigsegv; - - /* Flush instruction space. */ - // flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); - // tb_flush(env); } unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); return; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d2cc971143..cf4511b0e4 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -94,6 +94,10 @@ #include <linux/fiemap.h> #endif #include <linux/fb.h> +#if defined(CONFIG_USBFS) +#include <linux/usbdevice_fs.h> +#include <linux/usb/ch9.h> +#endif #include <linux/vt.h> #include <linux/dm-ioctl.h> #include <linux/reboot.h> @@ -4196,6 +4200,182 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp, 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) { diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 18d434d6dc..99bbce083c 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -863,6 +863,34 @@ struct target_pollfd { #define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap) +/* usb ioctls */ +#define TARGET_USBDEVFS_CONTROL TARGET_IOWRU('U', 0) +#define TARGET_USBDEVFS_BULK TARGET_IOWRU('U', 2) +#define TARGET_USBDEVFS_RESETEP TARGET_IORU('U', 3) +#define TARGET_USBDEVFS_SETINTERFACE TARGET_IORU('U', 4) +#define TARGET_USBDEVFS_SETCONFIGURATION TARGET_IORU('U', 5) +#define TARGET_USBDEVFS_GETDRIVER TARGET_IOWU('U', 8) +#define TARGET_USBDEVFS_SUBMITURB TARGET_IORU('U', 10) +#define TARGET_USBDEVFS_DISCARDURB TARGET_IO('U', 11) +#define TARGET_USBDEVFS_REAPURB TARGET_IOWU('U', 12) +#define TARGET_USBDEVFS_REAPURBNDELAY TARGET_IOWU('U', 13) +#define TARGET_USBDEVFS_DISCSIGNAL TARGET_IORU('U', 14) +#define TARGET_USBDEVFS_CLAIMINTERFACE TARGET_IORU('U', 15) +#define TARGET_USBDEVFS_RELEASEINTERFACE TARGET_IORU('U', 16) +#define TARGET_USBDEVFS_CONNECTINFO TARGET_IOWU('U', 17) +#define TARGET_USBDEVFS_IOCTL TARGET_IOWRU('U', 18) +#define TARGET_USBDEVFS_HUB_PORTINFO TARGET_IORU('U', 19) +#define TARGET_USBDEVFS_RESET TARGET_IO('U', 20) +#define TARGET_USBDEVFS_CLEAR_HALT TARGET_IORU('U', 21) +#define TARGET_USBDEVFS_DISCONNECT TARGET_IO('U', 22) +#define TARGET_USBDEVFS_CONNECT TARGET_IO('U', 23) +#define TARGET_USBDEVFS_CLAIM_PORT TARGET_IORU('U', 24) +#define TARGET_USBDEVFS_RELEASE_PORT TARGET_IORU('U', 25) +#define TARGET_USBDEVFS_GET_CAPABILITIES TARGET_IORU('U', 26) +#define TARGET_USBDEVFS_DISCONNECT_CLAIM TARGET_IORU('U', 27) +#define TARGET_USBDEVFS_DROP_PRIVILEGES TARGET_IOWU('U', 30) +#define TARGET_USBDEVFS_GET_SPEED TARGET_IO('U', 31) + /* cdrom commands */ #define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */ #define TARGET_CDROMRESUME 0x5302 /* Resume paused Audio Operation */ diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 24631b09be..b98a23b0f1 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -266,3 +266,71 @@ STRUCT(blkpg_ioctl_arg, TYPE_INT, /* flags */ TYPE_INT, /* datalen */ TYPE_PTRVOID) /* data */ + +#if defined(CONFIG_USBFS) +/* usb device ioctls */ +STRUCT(usbdevfs_ctrltransfer, + TYPE_CHAR, /* bRequestType */ + TYPE_CHAR, /* bRequest */ + TYPE_SHORT, /* wValue */ + TYPE_SHORT, /* wIndex */ + TYPE_SHORT, /* wLength */ + TYPE_INT, /* timeout */ + TYPE_PTRVOID) /* data */ + +STRUCT(usbdevfs_bulktransfer, + TYPE_INT, /* ep */ + TYPE_INT, /* len */ + TYPE_INT, /* timeout */ + TYPE_PTRVOID) /* data */ + +STRUCT(usbdevfs_setinterface, + TYPE_INT, /* interface */ + TYPE_INT) /* altsetting */ + +STRUCT(usbdevfs_disconnectsignal, + TYPE_INT, /* signr */ + TYPE_PTRVOID) /* context */ + +STRUCT(usbdevfs_getdriver, + TYPE_INT, /* interface */ + MK_ARRAY(TYPE_CHAR, USBDEVFS_MAXDRIVERNAME + 1)) /* driver */ + +STRUCT(usbdevfs_connectinfo, + TYPE_INT, /* devnum */ + TYPE_CHAR) /* slow */ + +STRUCT(usbdevfs_iso_packet_desc, + TYPE_INT, /* length */ + TYPE_INT, /* actual_length */ + TYPE_INT) /* status */ + +STRUCT(usbdevfs_urb, + TYPE_CHAR, /* type */ + TYPE_CHAR, /* endpoint */ + TYPE_INT, /* status */ + TYPE_INT, /* flags */ + TYPE_PTRVOID, /* buffer */ + TYPE_INT, /* buffer_length */ + TYPE_INT, /* actual_length */ + TYPE_INT, /* start_frame */ + TYPE_INT, /* union number_of_packets stream_id */ + TYPE_INT, /* error_count */ + TYPE_INT, /* signr */ + TYPE_PTRVOID, /* usercontext */ + MK_ARRAY(MK_STRUCT(STRUCT_usbdevfs_iso_packet_desc), 0)) /* desc */ + +STRUCT(usbdevfs_ioctl, + TYPE_INT, /* ifno */ + TYPE_INT, /* ioctl_code */ + TYPE_PTRVOID) /* data */ + +STRUCT(usbdevfs_hub_portinfo, + TYPE_CHAR, /* nports */ + MK_ARRAY(TYPE_CHAR, 127)) /* port */ + +STRUCT(usbdevfs_disconnect_claim, + TYPE_INT, /* interface */ + TYPE_INT, /* flags */ + MK_ARRAY(TYPE_CHAR, USBDEVFS_MAXDRIVERNAME + 1)) /* driver */ +#endif /* CONFIG_USBFS */ |