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)
This commit is contained in:
Brooks Davis 2021-12-17 21:28:14 +00:00
parent 45b48cbc2b
commit 0ec590d24e
3 changed files with 188 additions and 66 deletions

View File

@ -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 */

View File

@ -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 */
@ -1066,6 +1067,38 @@ ugen_fs_set_complete(struct usb_fifo *f, uint8_t index)
usb_fifo_wakeup(f);
}
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)
{
@ -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 @@ ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index)
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_copy_out_cancelled(struct usb_fs_endpoint *fs_ep_uptr)
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_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:

View File

@ -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 */