All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v3 0/3] Linux usermode emulation user mode USB driver support.
@ 2018-10-08 16:35 Cortland Tölva
  2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 1/3] linux-user: Check for Linux USBFS in configure Cortland Tölva
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Cortland Tölva @ 2018-10-08 16:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Cortland Tölva, Laurent Vivier

This patch series enables programs running under QEMU Linux user mode
emulation to implement user-space USB drivers via the USBFS ioctl()s.
Support is limited to control, bulk, and possibly interrupt transfers.

The series compiles for i386, ppc64, ppc64le, mips, mipsel, xtensa, and
xtensaeb with an armv7l host and an x86_64 host.  The i386-linux-user target is
tested working with a USB scanner driver on an armv7l host.  Additionally, a
patched copy of strace was used to verify the conversion for reaping.
Additionally, a MIPS binary of lsusb was run on armv7l host to check reaping
and other functionality across endianness.

Changes from v1:
  use check_include in configure
  move struct definitions to later patch where possible
  improve pointer cast to int compatibility
  remove unimplemented types for usb streams

Changes from v2:
  calculate ioctl arg size at runtime
  organize urb metadata with struct
  hold lock_user memory from submit until reap
  supersedes patch series 'linux-user: usbfs improvements'

Cortland Tölva (3):
  linux-user: Check for Linux USBFS in configure
  linux-user: Define ordinary usbfs ioctls.
  linux-user: Implement special usbfs ioctls.

 configure                  |  12 ++-
 linux-user/ioctls.h        |  46 ++++++++++++
 linux-user/syscall.c       | 180 +++++++++++++++++++++++++++++++++++++++++++++
 linux-user/syscall_defs.h  |  28 +++++++
 linux-user/syscall_types.h |  68 +++++++++++++++++
 5 files changed, 333 insertions(+), 1 deletion(-)

-- 
2.11.0

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v3 1/3] linux-user: Check for Linux USBFS in configure
  2018-10-08 16:35 [Qemu-devel] [PATCH v3 0/3] Linux usermode emulation user mode USB driver support Cortland Tölva
@ 2018-10-08 16:35 ` Cortland Tölva
  2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 2/3] linux-user: Define ordinary usbfs ioctls Cortland Tölva
  2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special " Cortland Tölva
  2 siblings, 0 replies; 10+ messages in thread
From: Cortland Tölva @ 2018-10-08 16:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Cortland Tölva, Laurent Vivier

In preparation for adding user mode emulation support for the
Linux usbfs interface, check for its kernel header.

Signed-off-by: Cortland Tölva <cst@tolva.net>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Message-Id: <20180925071228.32040-2-cst@tolva.net>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 configure | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index f89d293585..19d66bbdeb 100755
--- a/configure
+++ b/configure
@@ -4236,7 +4236,14 @@ if compile_prog "" "" ; then
   memfd=yes
 fi
 
-
+# check for usbfs
+have_usbfs=no
+if test "$linux_user" = "yes"; then
+  if check_include linux/usbdevice_fs.h; then
+    have_usbfs=yes
+  fi
+  have_usbfs=yes
+fi
 
 # check for fallocate
 fallocate=no
@@ -6350,6 +6357,9 @@ fi
 if test "$memfd" = "yes" ; then
   echo "CONFIG_MEMFD=y" >> $config_host_mak
 fi
+if test "$have_usbfs" = "yes" ; then
+  echo "CONFIG_USBFS=y" >> $config_host_mak
+fi
 if test "$fallocate" = "yes" ; then
   echo "CONFIG_FALLOCATE=y" >> $config_host_mak
 fi
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v3 2/3] linux-user: Define ordinary usbfs ioctls.
  2018-10-08 16:35 [Qemu-devel] [PATCH v3 0/3] Linux usermode emulation user mode USB driver support Cortland Tölva
  2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 1/3] linux-user: Check for Linux USBFS in configure Cortland Tölva
@ 2018-10-08 16:35 ` Cortland Tölva
  2018-10-12 19:07   ` Laurent Vivier
  2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special " Cortland Tölva
  2 siblings, 1 reply; 10+ messages in thread
From: Cortland Tölva @ 2018-10-08 16:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Cortland Tölva

Provide ioctl definitions for the generic thunk mechanism to
convert most usbfs calls.  Calculate arg size at runtime.

Signed-off-by: Cortland Tölva <cst@tolva.net>
---
Changes from v1:
  move some type definitions to patch 3/3
Changes from v2:
  calculate ioctl arg size at runtime

 linux-user/ioctls.h        | 38 ++++++++++++++++++++++++++++++++++++
 linux-user/syscall.c       |  3 +++
 linux-user/syscall_defs.h  | 24 +++++++++++++++++++++++
 linux-user/syscall_types.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 113 insertions(+)

diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
index 586c794639..92f6177f1d 100644
--- a/linux-user/ioctls.h
+++ b/linux-user/ioctls.h
@@ -131,6 +131,44 @@
      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(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/syscall.c b/linux-user/syscall.c
index ae3c0dfef7..2641260186 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -94,6 +94,9 @@
 #include <linux/fiemap.h>
 #endif
 #include <linux/fb.h>
+#if defined(CONFIG_USBFS)
+#include <linux/usbdevice_fs.h>
+#endif
 #include <linux/vt.h>
 #include <linux/dm-ioctl.h>
 #include <linux/reboot.h>
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 18d434d6dc..2daa5ebdcc 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -863,6 +863,30 @@ 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_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..6f64a8bdf7 100644
--- a/linux-user/syscall_types.h
+++ b/linux-user/syscall_types.h
@@ -266,3 +266,51 @@ 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_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 */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special usbfs ioctls.
  2018-10-08 16:35 [Qemu-devel] [PATCH v3 0/3] Linux usermode emulation user mode USB driver support Cortland Tölva
  2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 1/3] linux-user: Check for Linux USBFS in configure Cortland Tölva
  2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 2/3] linux-user: Define ordinary usbfs ioctls Cortland Tölva
@ 2018-10-08 16:35 ` Cortland Tölva
  2018-10-18 16:42   ` Cortland Setlow Tölva
  2018-10-18 18:48   ` Laurent Vivier
  2 siblings, 2 replies; 10+ messages in thread
From: Cortland Tölva @ 2018-10-08 16:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Cortland Tölva

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 and by running
the PowerPC lsusb utility on x86_64.  The discardurb ioctl is
not exercised in these tests.

Signed-off-by: Cortland Tölva <cst@tolva.net>
---
There are two alternatives for the strategy of holding lock_user on
memory from submit until reap.  v3 of this series tries to determine
the access permissions for user memory from endpoint direction, but
the logic for this is complex.  The first alternative is to request
write access.  If that fails, request read access.  If that fails, try
to submit the ioctl with no buffer - perhaps the user code filled in
fields the kernel will ignore.  The second alternative is to read user
memory into an allocated buffer, pass it to the kernel, and write back
to target memory only if the kernel indicates that writes occurred.

Changes from v1:
  improve pointer cast to int compatibility
  remove unimplemented types for usb streams
  struct definitions moved to this patch where possible

Changes from v2:
 organize urb thunk metadata in a struct
 hold lock_user from submit until discard
 fixes for 64-bit hosts

 linux-user/ioctls.h        |   8 ++
 linux-user/syscall.c       | 177 +++++++++++++++++++++++++++++++++++++++++++++
 linux-user/syscall_defs.h  |   4 +
 linux-user/syscall_types.h |  20 +++++
 4 files changed, 209 insertions(+)

diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
index 92f6177f1d..ae8951625f 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(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))
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 2641260186..9b7ea96cfb 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -96,6 +96,7 @@
 #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>
@@ -4199,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 2daa5ebdcc..99bbce083c 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -870,6 +870,10 @@ struct target_pollfd {
 #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)
diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h
index 6f64a8bdf7..b98a23b0f1 100644
--- a/linux-user/syscall_types.h
+++ b/linux-user/syscall_types.h
@@ -300,6 +300,26 @@ 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 */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v3 2/3] linux-user: Define ordinary usbfs ioctls.
  2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 2/3] linux-user: Define ordinary usbfs ioctls Cortland Tölva
@ 2018-10-12 19:07   ` Laurent Vivier
  0 siblings, 0 replies; 10+ messages in thread
From: Laurent Vivier @ 2018-10-12 19:07 UTC (permalink / raw)
  To: Cortland Tölva, qemu-devel

On 08/10/2018 18:35, Cortland Tölva wrote:
> Provide ioctl definitions for the generic thunk mechanism to
> convert most usbfs calls.  Calculate arg size at runtime.
> 
> Signed-off-by: Cortland Tölva <cst@tolva.net>
> ---
> Changes from v1:
>   move some type definitions to patch 3/3
> Changes from v2:
>   calculate ioctl arg size at runtime
> 
>  linux-user/ioctls.h        | 38 ++++++++++++++++++++++++++++++++++++
>  linux-user/syscall.c       |  3 +++
>  linux-user/syscall_defs.h  | 24 +++++++++++++++++++++++
>  linux-user/syscall_types.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 113 insertions(+)
> 

Reviewed-by: Laurent Vivier <laurent@vivier.eu>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special usbfs ioctls.
  2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special " Cortland Tölva
@ 2018-10-18 16:42   ` Cortland Setlow Tölva
  2018-10-18 18:15     ` Laurent Vivier
  2018-10-18 18:48   ` Laurent Vivier
  1 sibling, 1 reply; 10+ messages in thread
From: Cortland Setlow Tölva @ 2018-10-18 16:42 UTC (permalink / raw)
  To: Cortland Tölva; +Cc: qemu-devel, pbonzini, Laurent Vivier

ping, please review v3, patch 3/3.

http://patchwork.ozlabs.org/patch/980667/
On Mon, Oct 8, 2018 at 9:35 AM Cortland Tölva <cst@tolva.net> wrote:
>
> 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 and by running
> the PowerPC lsusb utility on x86_64.  The discardurb ioctl is
> not exercised in these tests.
>
> Signed-off-by: Cortland Tölva <cst@tolva.net>
> ---
> There are two alternatives for the strategy of holding lock_user on
> memory from submit until reap.  v3 of this series tries to determine
> the access permissions for user memory from endpoint direction, but
> the logic for this is complex.  The first alternative is to request
> write access.  If that fails, request read access.  If that fails, try
> to submit the ioctl with no buffer - perhaps the user code filled in
> fields the kernel will ignore.  The second alternative is to read user
> memory into an allocated buffer, pass it to the kernel, and write back
> to target memory only if the kernel indicates that writes occurred.
>
> Changes from v1:
>   improve pointer cast to int compatibility
>   remove unimplemented types for usb streams
>   struct definitions moved to this patch where possible
>
> Changes from v2:
>  organize urb thunk metadata in a struct
>  hold lock_user from submit until discard
>  fixes for 64-bit hosts
>
>  linux-user/ioctls.h        |   8 ++
>  linux-user/syscall.c       | 177 +++++++++++++++++++++++++++++++++++++++++++++
>  linux-user/syscall_defs.h  |   4 +
>  linux-user/syscall_types.h |  20 +++++
>  4 files changed, 209 insertions(+)
>
> diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
> index 92f6177f1d..ae8951625f 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(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))
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index 2641260186..9b7ea96cfb 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -96,6 +96,7 @@
>  #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>
> @@ -4199,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 2daa5ebdcc..99bbce083c 100644
> --- a/linux-user/syscall_defs.h
> +++ b/linux-user/syscall_defs.h
> @@ -870,6 +870,10 @@ struct target_pollfd {
>  #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)
> diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h
> index 6f64a8bdf7..b98a23b0f1 100644
> --- a/linux-user/syscall_types.h
> +++ b/linux-user/syscall_types.h
> @@ -300,6 +300,26 @@ 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 */
> --
> 2.11.0

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special usbfs ioctls.
  2018-10-18 16:42   ` Cortland Setlow Tölva
@ 2018-10-18 18:15     ` Laurent Vivier
  0 siblings, 0 replies; 10+ messages in thread
From: Laurent Vivier @ 2018-10-18 18:15 UTC (permalink / raw)
  To: Cortland Setlow Tölva; +Cc: pbonzini, qemu-devel

Le 18/10/2018 à 18:42, Cortland Setlow Tölva a écrit :
> ping, please review v3, patch 3/3.
> 
> http://patchwork.ozlabs.org/patch/980667/

I don't have enough time to have an indeep review of this patch.
As it looks good and it's an interesting new feature, I'm going to
applied it as-is to my merge branch.

Thanks,
Laurent

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special usbfs ioctls.
  2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special " Cortland Tölva
  2018-10-18 16:42   ` Cortland Setlow Tölva
@ 2018-10-18 18:48   ` Laurent Vivier
  2018-10-19  2:16     ` Cortland Setlow Tölva
  1 sibling, 1 reply; 10+ messages in thread
From: Laurent Vivier @ 2018-10-18 18:48 UTC (permalink / raw)
  To: Cortland Tölva, qemu-devel

Le 08/10/2018 à 18:35, Cortland Tölva a écrit :
> 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 and by running
> the PowerPC lsusb utility on x86_64.  The discardurb ioctl is
> not exercised in these tests.
> 
> Signed-off-by: Cortland Tölva <cst@tolva.net>
> ---
> There are two alternatives for the strategy of holding lock_user on
> memory from submit until reap.  v3 of this series tries to determine
> the access permissions for user memory from endpoint direction, but
> the logic for this is complex.  The first alternative is to request
> write access.  If that fails, request read access.  If that fails, try
> to submit the ioctl with no buffer - perhaps the user code filled in
> fields the kernel will ignore.  The second alternative is to read user
> memory into an allocated buffer, pass it to the kernel, and write back
> to target memory only if the kernel indicates that writes occurred.
> 
> Changes from v1:
>   improve pointer cast to int compatibility
>   remove unimplemented types for usb streams
>   struct definitions moved to this patch where possible
> 
> Changes from v2:
>  organize urb thunk metadata in a struct
>  hold lock_user from submit until discard
>  fixes for 64-bit hosts
> 
>  linux-user/ioctls.h        |   8 ++
>  linux-user/syscall.c       | 177 +++++++++++++++++++++++++++++++++++++++++++++
>  linux-user/syscall_defs.h  |   4 +
>  linux-user/syscall_types.h |  20 +++++
>  4 files changed, 209 insertions(+)
> 
> diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
> index 92f6177f1d..ae8951625f 100644
...
> index 2641260186..9b7ea96cfb 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -96,6 +96,7 @@
>  #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>
> @@ -4199,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);

Here the key of the hashtable seems to be the pointer to the host live_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);

And here the key is the pointer to the target_urb_adr

So I think urb_hashtable_insert()  should be:

    g_hash_table_insert(urb_hashtable, urb->target_urb_adr, urb);

and urb_hashtable_lookup() should be:

    return g_hash_table_lookup(urb_hashtable, target_urb_adr);
...
Did I miss something?

Thanks,
Laurent

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special usbfs ioctls.
  2018-10-18 18:48   ` Laurent Vivier
@ 2018-10-19  2:16     ` Cortland Setlow Tölva
  2018-10-19  7:13       ` Laurent Vivier
  0 siblings, 1 reply; 10+ messages in thread
From: Cortland Setlow Tölva @ 2018-10-19  2:16 UTC (permalink / raw)
  To: Laurent Vivier; +Cc: Cortland Tölva, qemu-devel

On Thu, Oct 18, 2018 at 11:48 AM Laurent Vivier <laurent@vivier.eu> wrote:
>
> Le 08/10/2018 à 18:35, Cortland Tölva a écrit :
> > 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 and by running
> > the PowerPC lsusb utility on x86_64.  The discardurb ioctl is
> > not exercised in these tests.
> >
> > Signed-off-by: Cortland Tölva <cst@tolva.net>
> > ---
> > There are two alternatives for the strategy of holding lock_user on
> > memory from submit until reap.  v3 of this series tries to determine
> > the access permissions for user memory from endpoint direction, but
> > the logic for this is complex.  The first alternative is to request
> > write access.  If that fails, request read access.  If that fails, try
> > to submit the ioctl with no buffer - perhaps the user code filled in
> > fields the kernel will ignore.  The second alternative is to read user
> > memory into an allocated buffer, pass it to the kernel, and write back
> > to target memory only if the kernel indicates that writes occurred.
> >
> > Changes from v1:
> >   improve pointer cast to int compatibility
> >   remove unimplemented types for usb streams
> >   struct definitions moved to this patch where possible
> >
> > Changes from v2:
> >  organize urb thunk metadata in a struct
> >  hold lock_user from submit until discard
> >  fixes for 64-bit hosts
> >
> >  linux-user/ioctls.h        |   8 ++
> >  linux-user/syscall.c       | 177 +++++++++++++++++++++++++++++++++++++++++++++
> >  linux-user/syscall_defs.h  |   4 +
> >  linux-user/syscall_types.h |  20 +++++
> >  4 files changed, 209 insertions(+)
> >
> > diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
> > index 92f6177f1d..ae8951625f 100644
> ...
> > index 2641260186..9b7ea96cfb 100644
> > --- a/linux-user/syscall.c
> > +++ b/linux-user/syscall.c
> > @@ -96,6 +96,7 @@
> >  #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>
> > @@ -4199,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);

g_int64_hash means this GHashTable expects to be given a pointer to an int64_t,
and the int64_t is used as the key.

> > +    }
> > +    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);
>
> Here the key of the hashtable seems to be the pointer to the host live_urb.
>

The key is pointed to by urb. It is the first member of the struct,
target_urb_adr.

> > +}
> > +
> > +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);
>
> And here the key is the pointer to the target_urb_adr
>

target_urb_adr is on the stack here, and it contains the key.  Both
g_hash_table_lookup
and g_hash_table_insert take a pointer to the key, which is an int64_t
in this code.

> So I think urb_hashtable_insert()  should be:
>
>     g_hash_table_insert(urb_hashtable, urb->target_urb_adr, urb);
>
> and urb_hashtable_lookup() should be:
>
>     return g_hash_table_lookup(urb_hashtable, target_urb_adr);
> ...
> Did I miss something?

My code might have been clearer if urb_hashtable_insert and
urb_hashtable_lookup had the same argument type.  However,
I did not want to treat target_urb_adr as the first element in a
struct live_urb until checking that it was tracked in the hashtable.

The thing we are looking up has to be a target-sized pointer, so I
cannot use the g_direct_hash with host-sized pointers as keys.
The next best option was to use int64_t as the key.

>
> Thanks,
> Laurent

Thanks,
Cortland.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special usbfs ioctls.
  2018-10-19  2:16     ` Cortland Setlow Tölva
@ 2018-10-19  7:13       ` Laurent Vivier
  0 siblings, 0 replies; 10+ messages in thread
From: Laurent Vivier @ 2018-10-19  7:13 UTC (permalink / raw)
  To: Cortland Setlow Tölva; +Cc: qemu-devel

Le 19/10/2018 à 04:16, Cortland Setlow Tölva a écrit :
> On Thu, Oct 18, 2018 at 11:48 AM Laurent Vivier <laurent@vivier.eu> wrote:
>>
>> Le 08/10/2018 à 18:35, Cortland Tölva a écrit :
>>> 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 and by running
>>> the PowerPC lsusb utility on x86_64.  The discardurb ioctl is
>>> not exercised in these tests.
>>>
>>> Signed-off-by: Cortland Tölva <cst@tolva.net>
>>> ---
>>> There are two alternatives for the strategy of holding lock_user on
>>> memory from submit until reap.  v3 of this series tries to determine
>>> the access permissions for user memory from endpoint direction, but
>>> the logic for this is complex.  The first alternative is to request
>>> write access.  If that fails, request read access.  If that fails, try
>>> to submit the ioctl with no buffer - perhaps the user code filled in
>>> fields the kernel will ignore.  The second alternative is to read user
>>> memory into an allocated buffer, pass it to the kernel, and write back
>>> to target memory only if the kernel indicates that writes occurred.
>>>
>>> Changes from v1:
>>>   improve pointer cast to int compatibility
>>>   remove unimplemented types for usb streams
>>>   struct definitions moved to this patch where possible
>>>
>>> Changes from v2:
>>>  organize urb thunk metadata in a struct
>>>  hold lock_user from submit until discard
>>>  fixes for 64-bit hosts
>>>
>>>  linux-user/ioctls.h        |   8 ++
>>>  linux-user/syscall.c       | 177 +++++++++++++++++++++++++++++++++++++++++++++
>>>  linux-user/syscall_defs.h  |   4 +
>>>  linux-user/syscall_types.h |  20 +++++
>>>  4 files changed, 209 insertions(+)
>>>
>>> diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
>>> index 92f6177f1d..ae8951625f 100644
>> ...
>>> index 2641260186..9b7ea96cfb 100644
>>> --- a/linux-user/syscall.c
>>> +++ b/linux-user/syscall.c
>>> @@ -96,6 +96,7 @@
>>>  #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>
>>> @@ -4199,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);
> 
> g_int64_hash means this GHashTable expects to be given a pointer to an int64_t,
> and the int64_t is used as the key.
> 
>>> +    }
>>> +    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);
>>
>> Here the key of the hashtable seems to be the pointer to the host live_urb.
>>
> 
> The key is pointed to by urb. It is the first member of the struct,
> target_urb_adr.
> 
>>> +}
>>> +
>>> +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);
>>
>> And here the key is the pointer to the target_urb_adr
>>
> 
> target_urb_adr is on the stack here, and it contains the key.  Both
> g_hash_table_lookup
> and g_hash_table_insert take a pointer to the key, which is an int64_t
> in this code.
> 
>> So I think urb_hashtable_insert()  should be:
>>
>>     g_hash_table_insert(urb_hashtable, urb->target_urb_adr, urb);
>>
>> and urb_hashtable_lookup() should be:
>>
>>     return g_hash_table_lookup(urb_hashtable, target_urb_adr);
>> ...
>> Did I miss something?
> 
> My code might have been clearer if urb_hashtable_insert and
> urb_hashtable_lookup had the same argument type.  However,
> I did not want to treat target_urb_adr as the first element in a
> struct live_urb until checking that it was tracked in the hashtable.
> 
> The thing we are looking up has to be a target-sized pointer, so I
> cannot use the g_direct_hash with host-sized pointers as keys.
> The next best option was to use int64_t as the key.

OK, it's clearer now. Thank you for the explanation.

Laurent

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2018-10-19  7:14 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-08 16:35 [Qemu-devel] [PATCH v3 0/3] Linux usermode emulation user mode USB driver support Cortland Tölva
2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 1/3] linux-user: Check for Linux USBFS in configure Cortland Tölva
2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 2/3] linux-user: Define ordinary usbfs ioctls Cortland Tölva
2018-10-12 19:07   ` Laurent Vivier
2018-10-08 16:35 ` [Qemu-devel] [PATCH v3 3/3] linux-user: Implement special " Cortland Tölva
2018-10-18 16:42   ` Cortland Setlow Tölva
2018-10-18 18:15     ` Laurent Vivier
2018-10-18 18:48   ` Laurent Vivier
2018-10-19  2:16     ` Cortland Setlow Tölva
2018-10-19  7:13       ` Laurent Vivier

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.