From patchwork Wed Sep 19 08:04:21 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Cortland_Setlow_T=C3=B6lva?= X-Patchwork-Id: 10605825 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A88511508 for ; Wed, 19 Sep 2018 12:51:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 981FE2BE27 for ; Wed, 19 Sep 2018 12:51:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8CAD12C0B8; Wed, 19 Sep 2018 12:51:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id D0F7A2BE27 for ; Wed, 19 Sep 2018 12:51:56 +0000 (UTC) Received: from localhost ([::1]:45280 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1g2bxT-0004Na-S9 for patchwork-qemu-devel@patchwork.kernel.org; Wed, 19 Sep 2018 08:51:55 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42136) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1g2XTx-0002Ij-QX for qemu-devel@nongnu.org; Wed, 19 Sep 2018 04:05:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1g2XTu-0000na-Km for qemu-devel@nongnu.org; Wed, 19 Sep 2018 04:05:09 -0400 Received: from mail2.static.mailgun.info ([104.130.122.2]:16570) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1g2XTu-0000eG-Ed for qemu-devel@nongnu.org; Wed, 19 Sep 2018 04:05:06 -0400 DKIM-Signature: a=rsa-sha256; v=1; c=relaxed/relaxed; d=mg-relay.tolva.net; q=dns/txt; s=pic; t=1537344306; h=Content-Transfer-Encoding: Content-Type: MIME-Version: References: In-Reply-To: Message-Id: Date: Subject: Cc: To: From: Sender; bh=GIWL4uVpKsvPaWTzeXVmEu8xVdbqY3mw53Et9aBOiK4=; b=MLBONYDgTQR91dEKrAO1RRZNifx1eUdR5u/Ij/W0Mn7o7/v0TIHQCNVmomuWhS6uS5YrNZpS PFfEcoSDzScOwa16LVI6prOwZJvpPQhB+FoWXPBrOtEwbC5AR7Co5rgnNjnJrls1tcY2TND4 JJliS1aGHbfCi8n4KYDmZxm1ihE= X-Mailgun-Sending-Ip: 104.130.122.2 X-Mailgun-Sid: WyI5MDdkOCIsICJxZW11LWRldmVsQG5vbmdudS5vcmciLCAiN2Q3MzI5Il0= Received: from cerberus.teal.tolva.net (c-73-252-167-134.hsd1.ca.comcast.net [73.252.167.134]) by mxa.mailgun.org with ESMTP id 5ba20331.7fd524084cb0-smtp-out-n03; Wed, 19 Sep 2018 08:05:05 -0000 (UTC) From: =?utf-8?q?Cortland_T=C3=B6lva?= To: qemu-devel@nongnu.org Date: Wed, 19 Sep 2018 01:04:21 -0700 Message-Id: <20180919080421.24011-4-cst@tolva.net> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180919080421.24011-1-cst@tolva.net> References: <20180919080421.24011-1-cst@tolva.net> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 104.130.122.2 X-Mailman-Approved-At: Wed, 19 Sep 2018 08:47:21 -0400 Subject: [Qemu-devel] [PATCH 3/3] linux-user: implement special usbfs ioctls. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Riku Voipio , Laurent Vivier , =?utf-8?q?Cortland_T=C3=B6lva?= Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Userspace submits a USB Request Buffer to the kernel, optionally discards it, and finally reaps the URB. Thunk buffers from target to host and back. Tested by running an i386 scanner driver on ARMv7. Neither the discardurb ioctl nor the kernel's updating the argument to the reap ioctl with a pointer to a reaped URB are exercised by this. Signed-off-by: Cortland Tölva Reviewed-by: Laurent Vivier --- linux-user/ioctls.h | 9 +++ linux-user/syscall.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 92f6177f1d..0118fa7e64 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -143,6 +143,14 @@ 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(MK_STRUCT(STRUCT_usbdevfs_urb))) + IOCTL_SPECIAL(USBDEVFS_REAPURBNDELAY, IOC_R, do_ioctl_usbdevfs_reapurb, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb))) IOCTL(USBDEVFS_DISCSIGNAL, IOC_W, MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnectsignal))) IOCTL(USBDEVFS_CLAIMINTERFACE, IOC_W, MK_PTR(TYPE_INT)) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 39f21b78c8..b66688277b 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5491,6 +5491,174 @@ 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 +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 abi_long +do_ioctl_usbdevfs_reapurb(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, int cmd, abi_long arg) +{ + const argtype *arg_type = ie->arg_type; + GHashTable * const urb_hash = usbdevfs_urb_hashtable(); + const argtype ptrvoid_arg_type[] = { TYPE_PTRVOID, 0, 0 }; + int target_size; + int64_t reaped_userurb; + int64_t target_urbptr; + uintptr_t target_urbptr_ptr; + char *tagged_urb; + void *argptr; + abi_long ret; + + target_size = thunk_type_size(++arg_type, THUNK_TARGET); + + ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp)); + if (is_error(ret)) { + return ret; + } + + memcpy(&reaped_userurb, buf_temp, sizeof(int64_t)); + tagged_urb = ((char *)(uintptr_t)reaped_userurb) - sizeof(int64_t); + memcpy(&target_urbptr, tagged_urb, sizeof(int64_t)); + if (!target_urbptr) { + return -TARGET_EFAULT; + } + g_hash_table_remove(urb_hash, tagged_urb); + + argptr = lock_user(VERIFY_WRITE, (abi_long) target_urbptr, target_size, 0); + if (!argptr) { + g_free(tagged_urb); + return -TARGET_EFAULT; + } + thunk_convert(argptr, (char *)(uintptr_t)reaped_userurb, arg_type, + THUNK_TARGET); + unlock_user(argptr, target_urbptr, target_size); + + target_size = thunk_type_size(ptrvoid_arg_type, THUNK_TARGET); + argptr = lock_user(VERIFY_WRITE, (abi_long)arg, target_size, 0); + if (!argptr) { + g_free(tagged_urb); + return -TARGET_EFAULT; + } + target_urbptr_ptr = (uintptr_t) target_urbptr; + thunk_convert(argptr, &target_urbptr_ptr, ptrvoid_arg_type, THUNK_TARGET); + unlock_user(argptr, (abi_long) arg, target_size); + g_free(tagged_urb); + 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) +{ + GHashTable * const urb_hash = usbdevfs_urb_hashtable(); + abi_long host_urb; + int64_t tag_urb_key; + char *tagged_urb; + + /* map target pointer back to host tagged URB. */ + tag_urb_key = (int64_t) arg; + tagged_urb = g_hash_table_lookup(urb_hash, &tag_urb_key); + if (!tagged_urb) { + return -TARGET_EFAULT; + } + /* offset from tag to urb */ + host_urb = (abi_long) (tagged_urb + sizeof(int64_t)); + return get_errno(safe_ioctl(fd, ie->host_cmd, host_urb)); +} + +static int convert_iso_packets(uint8_t *dst, int totlen, abi_long src) +{ + void *srcptr; + int host_size, target_size; + int i, iso_packets; + const argtype arg_type[] = { MK_STRUCT(STRUCT_usbdevfs_iso_packet_desc) }; + + if (((struct usbdevfs_urb *)dst)->type == USBDEVFS_URB_TYPE_ISO) { + iso_packets = ((struct usbdevfs_urb *)dst)->number_of_packets; + } else { + iso_packets = 0; + } + + host_size = thunk_type_size(arg_type, THUNK_HOST); + target_size = thunk_type_size(arg_type, THUNK_TARGET); + + for (i = 0; i < iso_packets; ++i) { + if ((totlen + host_size) >= MAX_STRUCT_SIZE) { + break; + } + srcptr = lock_user(VERIFY_READ, src, target_size, 1); + thunk_convert(dst + totlen, srcptr, arg_type, THUNK_HOST); + unlock_user(srcptr, src, 0); + src += target_size; + totlen += host_size; + } + return totlen; +} + +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; + int host_size; + abi_long ret; + char *tagged_urb = NULL; + void *argptr; + int64_t arg_tag; + GHashTable * const urb_hash = usbdevfs_urb_hashtable(); + + /* + * 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++; + host_size = thunk_type_size(arg_type, THUNK_HOST); + target_size = thunk_type_size(arg_type, THUNK_TARGET); + + 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_size = convert_iso_packets(buf_temp, host_size, arg + target_size); + + /* allocate extra space for a tag. */ + tagged_urb = g_try_malloc0(host_size + sizeof(int64_t)); + if (!tagged_urb) { + return -TARGET_ENOMEM; + } + memcpy(&tagged_urb[sizeof(int64_t)], buf_temp, host_size); + + ret = get_errno(safe_ioctl(fd, ie->host_cmd, &tagged_urb[sizeof(int64_t)])); + if (is_error(ret)) { + g_free(tagged_urb); + } else { + arg_tag = (int64_t) arg; + memcpy(tagged_urb, &arg_tag, sizeof(int64_t)); + g_hash_table_insert(urb_hash, tagged_urb, tagged_urb); + } + + 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) {