aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrooks Davis <brooks@FreeBSD.org>2021-12-17 21:28:14 +0000
committerBrooks Davis <brooks@FreeBSD.org>2021-12-17 21:28:14 +0000
commit0ec590d24e415dd36e38648630a0b963412ad87e (patch)
treea7dde475b61604cd39a5766f1f764565a02f3f5d
parent45b48cbc2b5819cd6e3dee3632d66e55d5d7c101 (diff)
downloadsrc-0ec590d24e415dd36e38648630a0b963412ad87e.tar.gz
src-0ec590d24e415dd36e38648630a0b963412ad87e.zip
usb: add 32-bit compat for FIFOs
Unlike most 32-bit compatability code, this isn't just a simple thunk in the ioctl code. An ioctl (USB_FS_INIT) is used to install a pointer to an array of usb_fs_endpoint structs which are then used by the ugen fifo code. These struct contains an array of pointers which requires translation. We change the interfaces around struct usb_fs_endpoint as follows: - We store the size of struct usb_fs_endpoint in struct usb_fifo in the USB_FS_INIT handler so we know the ABI of the userspace array. - APIs to manipulate userspace struct usb_fs_endpoint objects now take a struct usb_fifo and an index rather than a pointer to the object. This allows most code to remain oblivious to the different struct usb_fs_endpoint sizes. - Add ugen_fs_copyin() which copies the struct usb_fs_endpoint from userspace, thunking it to the native size if required. - Uses of struct usb_fs_endpoint's ppBuffer member are now via ugen_fs_getbuffer() which produces a native pointer. - Updates to userspace are now handled by ugen_fs_update(). For clarity, single, fixed-sized members now are accessed with fueword/suword rather than copyin/copyout. Reviewed by: hselasky, jrtc27 (prior version)
-rw-r--r--sys/dev/usb/usb_dev.h1
-rw-r--r--sys/dev/usb/usb_generic.c235
-rw-r--r--sys/dev/usb/usb_ioctl.h18
3 files changed, 188 insertions, 66 deletions
diff --git a/sys/dev/usb/usb_dev.h b/sys/dev/usb/usb_dev.h
index ae851153efb1..1bdfa46d064f 100644
--- a/sys/dev/usb/usb_dev.h
+++ b/sys/dev/usb/usb_dev.h
@@ -123,6 +123,7 @@ struct usb_fifo {
void *priv_sc0; /* client data */
void *priv_sc1; /* client data */
void *queue_data;
+ usb_size_t fs_ep_sz;
usb_timeout_t timeout; /* timeout in milliseconds */
usb_frlength_t bufsize; /* BULK and INTERRUPT buffer size */
usb_frcount_t nframes; /* for isochronous mode */
diff --git a/sys/dev/usb/usb_generic.c b/sys/dev/usb/usb_generic.c
index 746a843baf09..5efab44d0efd 100644
--- a/sys/dev/usb/usb_generic.c
+++ b/sys/dev/usb/usb_generic.c
@@ -119,6 +119,7 @@ static int ugen_re_enumerate(struct usb_fifo *);
static int ugen_iface_ioctl(struct usb_fifo *, u_long, void *, int);
static uint8_t ugen_fs_get_complete(struct usb_fifo *, uint8_t *);
static int ugen_fs_uninit(struct usb_fifo *f);
+static int ugen_fs_copyin(struct usb_fifo *, uint8_t, struct usb_fs_endpoint*);
/* structures */
@@ -1067,6 +1068,38 @@ ugen_fs_set_complete(struct usb_fifo *f, uint8_t index)
}
static int
+ugen_fs_getbuffer(void **uptrp, struct usb_fifo *f, void *buffer,
+ usb_frcount_t n)
+{
+ union {
+ void **ppBuffer;
+#ifdef COMPAT_FREEBSD32
+ uint32_t *ppBuffer32;
+#endif
+ } u;
+#ifdef COMPAT_FREEBSD32
+ uint32_t uptr32;
+#endif
+
+ u.ppBuffer = buffer;
+ switch (f->fs_ep_sz) {
+ case sizeof(struct usb_fs_endpoint):
+ if (fueword(u.ppBuffer + n, (uintptr_t *)uptrp) != 0)
+ return (EFAULT);
+ return (0);
+#ifdef COMPAT_FREEBSD32
+ case sizeof(struct usb_fs_endpoint32):
+ if (fueword32(u.ppBuffer32 + n, &uptr32) != 0)
+ return (EFAULT);
+ *uptrp = PTRIN(uptr32);
+ return (0);
+#endif
+ default:
+ panic("%s: unhandled fs_ep_sz %#x", __func__, f->fs_ep_sz);
+ }
+}
+
+static int
ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index)
{
struct usb_device_request *req;
@@ -1095,8 +1128,7 @@ ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index)
}
mtx_unlock(f->priv_mtx);
- error = copyin(f->fs_ep_ptr +
- ep_index, &fs_ep, sizeof(fs_ep));
+ error = ugen_fs_copyin(f, ep_index, &fs_ep);
if (error) {
return (error);
}
@@ -1110,8 +1142,7 @@ ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index)
xfer->error = USB_ERR_INVAL;
goto complete;
}
- error = copyin(fs_ep.ppBuffer,
- &uaddr, sizeof(uaddr));
+ error = ugen_fs_getbuffer(&uaddr, f, fs_ep.ppBuffer, 0);
if (error) {
return (error);
}
@@ -1121,10 +1152,8 @@ ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index)
if (xfer->flags_int.control_xfr) {
req = xfer->frbuffers[0].buffer;
- error = copyin(fs_ep.pLength,
- &length, sizeof(length));
- if (error) {
- return (error);
+ if (fueword32(fs_ep.pLength, &length) != 0) {
+ return (EFAULT);
}
if (length != sizeof(*req)) {
xfer->error = USB_ERR_INVAL;
@@ -1190,9 +1219,7 @@ ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index)
xfer->flags.stall_pipe = 0;
for (; n != xfer->nframes; n++) {
- error = copyin(fs_ep.pLength + n,
- &length, sizeof(length));
- if (error) {
+ if (fueword32(fs_ep.pLength + n, &length) != 0) {
break;
}
usbd_xfer_set_frame_len(xfer, n, length);
@@ -1205,8 +1232,7 @@ ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index)
if (!isread) {
/* we need to know the source buffer */
- error = copyin(fs_ep.ppBuffer + n,
- &uaddr, sizeof(uaddr));
+ error = ugen_fs_getbuffer(&uaddr, f, fs_ep.ppBuffer, n);
if (error) {
break;
}
@@ -1239,13 +1265,109 @@ complete:
return (0);
}
+static struct usb_fs_endpoint *
+ugen_fs_ep_uptr(struct usb_fifo *f, uint8_t ep_index)
+{
+ return ((struct usb_fs_endpoint *)
+ ((char *)f->fs_ep_ptr + (ep_index * f->fs_ep_sz)));
+}
+
+static int
+ugen_fs_copyin(struct usb_fifo *f, uint8_t ep_index,
+ struct usb_fs_endpoint* fs_ep)
+{
+#ifdef COMPAT_FREEBSD32
+ struct usb_fs_endpoint32 fs_ep32;
+#endif
+ int error;
+
+ switch (f->fs_ep_sz) {
+ case sizeof(struct usb_fs_endpoint):
+ error = copyin(ugen_fs_ep_uptr(f, ep_index), fs_ep,
+ f->fs_ep_sz);
+ if (error != 0)
+ return (error);
+ break;
+
+#ifdef COMPAT_FREEBSD32
+ case sizeof(struct usb_fs_endpoint32):
+ error = copyin(ugen_fs_ep_uptr(f, ep_index), &fs_ep32,
+ f->fs_ep_sz);
+ if (error != 0)
+ return (error);
+ PTRIN_CP(fs_ep32, *fs_ep, ppBuffer);
+ PTRIN_CP(fs_ep32, *fs_ep, pLength);
+ CP(fs_ep32, *fs_ep, nFrames);
+ CP(fs_ep32, *fs_ep, aFrames);
+ CP(fs_ep32, *fs_ep, flags);
+ CP(fs_ep32, *fs_ep, timeout);
+ CP(fs_ep32, *fs_ep, isoc_time_complete);
+ CP(fs_ep32, *fs_ep, status);
+ break;
+#endif
+ default:
+ panic("%s: unhandled fs_ep_sz %#x", __func__, f->fs_ep_sz);
+ }
+
+ return (0);
+}
+
static int
-ugen_fs_copy_out_cancelled(struct usb_fs_endpoint *fs_ep_uptr)
+ugen_fs_update(const struct usb_fs_endpoint *fs_ep,
+ struct usb_fifo *f, uint8_t ep_index)
+{
+ union {
+ struct usb_fs_endpoint *fs_ep_uptr;
+#ifdef COMPAT_FREEBSD32
+ struct usb_fs_endpoint32 *fs_ep_uptr32;
+#endif
+ } u;
+ uint32_t *aFrames_uptr;
+ uint16_t *isoc_time_complete_uptr;
+ int *status_uptr;
+
+ switch (f->fs_ep_sz) {
+ case sizeof(struct usb_fs_endpoint):
+ u.fs_ep_uptr = ugen_fs_ep_uptr(f, ep_index);
+ aFrames_uptr = &u.fs_ep_uptr->aFrames;
+ isoc_time_complete_uptr = &u.fs_ep_uptr->isoc_time_complete;
+ status_uptr = &u.fs_ep_uptr->status;
+ break;
+#ifdef COMPAT_FREEBSD32
+ case sizeof(struct usb_fs_endpoint32):
+ u.fs_ep_uptr32 = (struct usb_fs_endpoint32 *)
+ ugen_fs_ep_uptr(f, ep_index);
+ aFrames_uptr = &u.fs_ep_uptr32->aFrames;
+ isoc_time_complete_uptr = &u.fs_ep_uptr32->isoc_time_complete;
+ status_uptr = &u.fs_ep_uptr32->status;
+ break;
+#endif
+ default:
+ panic("%s: unhandled fs_ep_sz %#x", __func__, f->fs_ep_sz);
+ }
+
+ /* update "aFrames" */
+ if (suword32(aFrames_uptr, fs_ep->aFrames) != 0)
+ return (EFAULT);
+
+ /* update "isoc_time_complete" */
+ if (suword16(isoc_time_complete_uptr, fs_ep->isoc_time_complete) != 0)
+ return (EFAULT);
+
+ /* update "status" */
+ if (suword32(status_uptr, fs_ep->status) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+ugen_fs_copy_out_cancelled(struct usb_fifo *f, uint8_t ep_index)
{
struct usb_fs_endpoint fs_ep;
int error;
- error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep));
+ error = ugen_fs_copyin(f, ep_index, &fs_ep);
if (error)
return (error);
@@ -1253,24 +1375,7 @@ ugen_fs_copy_out_cancelled(struct usb_fs_endpoint *fs_ep_uptr)
fs_ep.aFrames = 0;
fs_ep.isoc_time_complete = 0;
- /* update "aFrames" */
- error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames,
- sizeof(fs_ep.aFrames));
- if (error)
- goto done;
-
- /* update "isoc_time_complete" */
- error = copyout(&fs_ep.isoc_time_complete,
- &fs_ep_uptr->isoc_time_complete,
- sizeof(fs_ep.isoc_time_complete));
- if (error)
- goto done;
-
- /* update "status" */
- error = copyout(&fs_ep.status, &fs_ep_uptr->status,
- sizeof(fs_ep.status));
-done:
- return (error);
+ return (ugen_fs_update(&fs_ep, f, ep_index));
}
static int
@@ -1279,7 +1384,6 @@ ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index)
struct usb_device_request *req;
struct usb_xfer *xfer;
struct usb_fs_endpoint fs_ep;
- struct usb_fs_endpoint *fs_ep_uptr; /* userland ptr */
void *uaddr; /* userland ptr */
void *kaddr;
usb_frlength_t offset;
@@ -1302,18 +1406,18 @@ ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index)
!xfer->flags_int.started) {
mtx_unlock(f->priv_mtx);
DPRINTF("Returning fake cancel event\n");
- return (ugen_fs_copy_out_cancelled(f->fs_ep_ptr + ep_index));
+ return (ugen_fs_copy_out_cancelled(f, ep_index));
} else if (usbd_transfer_pending(xfer)) {
mtx_unlock(f->priv_mtx);
return (EBUSY); /* should not happen */
}
mtx_unlock(f->priv_mtx);
- fs_ep_uptr = f->fs_ep_ptr + ep_index;
- error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep));
+ error = ugen_fs_copyin(f, ep_index, &fs_ep);
if (error) {
return (error);
}
+
fs_ep.status = xfer->error;
fs_ep.aFrames = xfer->aframes;
fs_ep.isoc_time_complete = xfer->isoc_time_complete;
@@ -1350,10 +1454,8 @@ ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index)
for (; n != xfer->nframes; n++) {
/* get initial length into "temp" */
- error = copyin(fs_ep.pLength + n,
- &temp, sizeof(temp));
- if (error) {
- return (error);
+ if (fueword32(fs_ep.pLength + n, &temp) != 0) {
+ return (EFAULT);
}
if (temp > rem) {
/* the userland length has been corrupted */
@@ -1375,8 +1477,7 @@ ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index)
}
if (isread) {
/* we need to know the destination buffer */
- error = copyin(fs_ep.ppBuffer + n,
- &uaddr, sizeof(uaddr));
+ error = ugen_fs_getbuffer(&uaddr, f, fs_ep.ppBuffer, n);
if (error) {
return (error);
}
@@ -1392,7 +1493,7 @@ ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index)
/* move data */
error = copyout(kaddr, uaddr, length);
if (error) {
- return (error);
+ goto complete;
}
}
/*
@@ -1402,31 +1503,13 @@ ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index)
offset += temp;
/* update length */
- error = copyout(&length,
- fs_ep.pLength + n, sizeof(length));
- if (error) {
- return (error);
- }
+ if (suword32(fs_ep.pLength + n, length) != 0)
+ goto complete;
}
complete:
- /* update "aFrames" */
- error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames,
- sizeof(fs_ep.aFrames));
- if (error)
- goto done;
-
- /* update "isoc_time_complete" */
- error = copyout(&fs_ep.isoc_time_complete,
- &fs_ep_uptr->isoc_time_complete,
- sizeof(fs_ep.isoc_time_complete));
- if (error)
- goto done;
-
- /* update "status" */
- error = copyout(&fs_ep.status, &fs_ep_uptr->status,
- sizeof(fs_ep.status));
-done:
+ if (error == 0)
+ error = ugen_fs_update(&fs_ep, f, ep_index);
return (error);
}
@@ -2126,6 +2209,9 @@ ugen_iface_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
static int
ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
{
+#ifdef COMPAT_FREEBSD32
+ struct usb_fs_init local_pinit;
+#endif
union {
struct usb_interface_descriptor *idesc;
struct usb_alt_interface *ai;
@@ -2133,6 +2219,9 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
struct usb_config_descriptor *cdesc;
struct usb_device_stats *stat;
struct usb_fs_init *pinit;
+#ifdef COMPAT_FREEBSD32
+ struct usb_fs_init32 *pinit32;
+#endif
struct usb_fs_uninit *puninit;
struct usb_device_port_path *dpp;
uint32_t *ptime;
@@ -2142,6 +2231,7 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
struct usb_device_descriptor *dtemp;
struct usb_config_descriptor *ctemp;
struct usb_interface *iface;
+ size_t usb_fs_endpoint_sz = sizeof(struct usb_fs_endpoint);
int error = 0;
uint8_t n;
@@ -2149,6 +2239,18 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
DPRINTFN(6, "cmd=0x%08lx\n", cmd);
+#ifdef COMPAT_FREEBSD32
+ switch (cmd) {
+ case USB_FS_INIT32:
+ PTRIN_CP(*u.pinit32, local_pinit, pEndpoints);
+ CP(*u.pinit32, local_pinit, ep_index_max);
+ u.addr = &local_pinit;
+ cmd = _IOC_NEWTYPE(USB_FS_INIT, struct usb_fs_init);
+ usb_fs_endpoint_sz = sizeof(struct usb_fs_endpoint32);
+ break;
+ }
+#endif
+
switch (cmd) {
case USB_DISCOVER:
usb_needs_explore_all();
@@ -2376,6 +2478,7 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO);
f->fs_ep_max = u.pinit->ep_index_max;
f->fs_ep_ptr = u.pinit->pEndpoints;
+ f->fs_ep_sz = usb_fs_endpoint_sz;
break;
case USB_FS_UNINIT:
diff --git a/sys/dev/usb/usb_ioctl.h b/sys/dev/usb/usb_ioctl.h
index 9d35588f1138..5a139d0653c5 100644
--- a/sys/dev/usb/usb_ioctl.h
+++ b/sys/dev/usb/usb_ioctl.h
@@ -400,6 +400,24 @@ void usb_gen_descriptor_from32(struct usb_gen_descriptor *ugd,
void update_usb_gen_descriptor32(struct usb_gen_descriptor32 *ugd32,
struct usb_gen_descriptor *ugd);
+struct usb_fs_endpoint32 {
+ uint32_t ppBuffer; /* void ** */
+ uint32_t pLength; /* uint32_t * */
+ uint32_t nFrames;
+ uint32_t aFrames;
+ uint16_t flags;
+ uint16_t timeout;
+ uint16_t isoc_time_complete;
+ int status;
+};
+
+struct usb_fs_init32 {
+ uint32_t pEndpoints; /* struct usb_fs_endpoint32 * */
+ uint8_t ep_index_max;
+};
+
+#define USB_FS_INIT32 _IOC_NEWTYPE(USB_FS_INIT, struct usb_fs_init32)
+
#endif /* COMPAT_FREEBSD32 */
#endif /* _KERNEL */