Index: stable/11/sys/amd64/linux/linux_ptrace.c =================================================================== --- stable/11/sys/amd64/linux/linux_ptrace.c (revision 332616) +++ stable/11/sys/amd64/linux/linux_ptrace.c (revision 332617) @@ -1,414 +1,416 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2017 Edward Tomasz Napierala * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #define LINUX_PTRACE_TRACEME 0 #define LINUX_PTRACE_PEEKTEXT 1 #define LINUX_PTRACE_PEEKDATA 2 #define LINUX_PTRACE_PEEKUSER 3 #define LINUX_PTRACE_POKETEXT 4 #define LINUX_PTRACE_POKEDATA 5 #define LINUX_PTRACE_POKEUSER 6 #define LINUX_PTRACE_CONT 7 #define LINUX_PTRACE_KILL 8 #define LINUX_PTRACE_SINGLESTEP 9 #define LINUX_PTRACE_GETREGS 12 #define LINUX_PTRACE_SETREGS 13 #define LINUX_PTRACE_GETFPREGS 14 #define LINUX_PTRACE_SETFPREGS 15 #define LINUX_PTRACE_ATTACH 16 #define LINUX_PTRACE_DETACH 17 #define LINUX_PTRACE_SYSCALL 24 #define LINUX_PTRACE_SETOPTIONS 0x4200 #define LINUX_PTRACE_GETREGSET 0x4204 #define LINUX_PTRACE_SEIZE 0x4206 #define LINUX_PTRACE_O_TRACESYSGOOD 1 #define LINUX_PTRACE_O_TRACEFORK 2 #define LINUX_PTRACE_O_TRACEVFORK 4 #define LINUX_PTRACE_O_TRACECLONE 8 #define LINUX_PTRACE_O_TRACEEXEC 16 #define LINUX_PTRACE_O_TRACEVFORKDONE 32 #define LINUX_PTRACE_O_TRACEEXIT 64 #define LINUX_PTRACE_O_TRACESECCOMP 128 #define LINUX_PTRACE_O_EXITKILL 1048576 #define LINUX_PTRACE_O_SUSPEND_SECCOMP 2097152 #define LINUX_NT_PRSTATUS 1 #define LINUX_PTRACE_O_MASK (LINUX_PTRACE_O_TRACESYSGOOD | \ LINUX_PTRACE_O_TRACEFORK | LINUX_PTRACE_O_TRACEVFORK | \ LINUX_PTRACE_O_TRACECLONE | LINUX_PTRACE_O_TRACEEXEC | \ LINUX_PTRACE_O_TRACEVFORKDONE | LINUX_PTRACE_O_TRACEEXIT | \ LINUX_PTRACE_O_TRACESECCOMP | LINUX_PTRACE_O_EXITKILL | \ LINUX_PTRACE_O_SUSPEND_SECCOMP) static int map_signum(int lsig, int *bsigp) { int bsig; if (lsig == 0) { *bsigp = 0; return (0); } if (lsig < 0 || lsig > LINUX_SIGRTMAX) return (EINVAL); bsig = linux_to_bsd_signal(lsig); if (bsig == SIGSTOP) bsig = 0; *bsigp = bsig; return (0); } struct linux_pt_reg { l_ulong r15; l_ulong r14; l_ulong r13; l_ulong r12; l_ulong rbp; l_ulong rbx; l_ulong r11; l_ulong r10; l_ulong r9; l_ulong r8; l_ulong rax; l_ulong rcx; l_ulong rdx; l_ulong rsi; l_ulong rdi; l_ulong orig_rax; l_ulong rip; l_ulong cs; l_ulong eflags; l_ulong rsp; l_ulong ss; }; /* * Translate amd64 ptrace registers between Linux and FreeBSD formats. * The translation is pretty straighforward, for all registers but * orig_rax on Linux side and r_trapno and r_err in FreeBSD. */ static void map_regs_to_linux(struct reg *b_reg, struct linux_pt_reg *l_reg) { l_reg->r15 = b_reg->r_r15; l_reg->r14 = b_reg->r_r14; l_reg->r13 = b_reg->r_r13; l_reg->r12 = b_reg->r_r12; l_reg->rbp = b_reg->r_rbp; l_reg->rbx = b_reg->r_rbx; l_reg->r11 = b_reg->r_r11; l_reg->r10 = b_reg->r_r10; l_reg->r9 = b_reg->r_r9; l_reg->r8 = b_reg->r_r8; l_reg->rax = b_reg->r_rax; l_reg->rcx = b_reg->r_rcx; l_reg->rdx = b_reg->r_rdx; l_reg->rsi = b_reg->r_rsi; l_reg->rdi = b_reg->r_rdi; l_reg->orig_rax = b_reg->r_rax; l_reg->rip = b_reg->r_rip; l_reg->cs = b_reg->r_cs; l_reg->eflags = b_reg->r_rflags; l_reg->rsp = b_reg->r_rsp; l_reg->ss = b_reg->r_ss; } static void map_regs_from_linux(struct reg *b_reg, struct linux_pt_reg *l_reg) { b_reg->r_r15 = l_reg->r15; b_reg->r_r14 = l_reg->r14; b_reg->r_r13 = l_reg->r13; b_reg->r_r12 = l_reg->r12; b_reg->r_r11 = l_reg->r11; b_reg->r_r10 = l_reg->r10; b_reg->r_r9 = l_reg->r9; b_reg->r_r8 = l_reg->r8; b_reg->r_rdi = l_reg->rdi; b_reg->r_rsi = l_reg->rsi; b_reg->r_rbp = l_reg->rbp; b_reg->r_rbx = l_reg->rbx; b_reg->r_rdx = l_reg->rdx; b_reg->r_rcx = l_reg->rcx; b_reg->r_rax = l_reg->rax; /* * XXX: Are zeroes the right thing to put here? */ b_reg->r_trapno = 0; b_reg->r_fs = 0; b_reg->r_gs = 0; b_reg->r_err = 0; b_reg->r_es = 0; b_reg->r_ds = 0; b_reg->r_rip = l_reg->rip; b_reg->r_cs = l_reg->cs; b_reg->r_rflags = l_reg->eflags; b_reg->r_rsp = l_reg->rsp; b_reg->r_ss = l_reg->ss; } static int linux_ptrace_peek(struct thread *td, pid_t pid, void *addr, void *data) { int error; error = kern_ptrace(td, PT_READ_I, pid, addr, 0); if (error == 0) error = copyout(td->td_retval, data, sizeof(l_int)); td->td_retval[0] = error; return (error); } static int linux_ptrace_setoptions(struct thread *td, pid_t pid, l_ulong data) { int mask; mask = 0; if (data & ~LINUX_PTRACE_O_MASK) { printf("%s: unknown ptrace option %lx set; " "returning EINVAL\n", __func__, data & ~LINUX_PTRACE_O_MASK); return (EINVAL); } /* * PTRACE_O_EXITKILL is ignored, we do that by default. */ if (data & LINUX_PTRACE_O_TRACESYSGOOD) { printf("%s: PTRACE_O_TRACESYSGOOD not implemented; " "returning EINVAL\n", __func__); return (EINVAL); } if (data & LINUX_PTRACE_O_TRACEFORK) mask |= PTRACE_FORK; if (data & LINUX_PTRACE_O_TRACEVFORK) mask |= PTRACE_VFORK; if (data & LINUX_PTRACE_O_TRACECLONE) mask |= PTRACE_VFORK; if (data & LINUX_PTRACE_O_TRACEEXEC) mask |= PTRACE_EXEC; if (data & LINUX_PTRACE_O_TRACEVFORKDONE) mask |= PTRACE_VFORK; /* XXX: Close enough? */ if (data & LINUX_PTRACE_O_TRACEEXIT) { printf("%s: PTRACE_O_TRACEEXIT not implemented; " "returning EINVAL\n", __func__); return (EINVAL); } return (kern_ptrace(td, PT_SET_EVENT_MASK, pid, &mask, sizeof(mask))); } static int linux_ptrace_getregs(struct thread *td, pid_t pid, void *data) { struct ptrace_lwpinfo lwpinfo; struct reg b_reg; struct linux_pt_reg l_reg; int error; error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); if (error != 0) return (error); map_regs_to_linux(&b_reg, &l_reg); /* * The strace(1) utility depends on RAX being set to -ENOSYS * on syscall entry. */ error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); if (error != 0) { printf("%s: PT_LWPINFO failed with error %d\n", __func__, error); return (error); } if (lwpinfo.pl_flags & PL_FLAG_SCE) l_reg.rax = -38; // XXX: Don't hardcode? error = copyout(&l_reg, (void *)data, sizeof(l_reg)); return (error); } static int linux_ptrace_setregs(struct thread *td, pid_t pid, void *data) { struct reg b_reg; struct linux_pt_reg l_reg; int error; error = copyin(data, &l_reg, sizeof(l_reg)); if (error != 0) return (error); map_regs_from_linux(&b_reg, &l_reg); error = kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0); return (error); } static int linux_ptrace_getregset(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) { switch (addr) { case LINUX_NT_PRSTATUS: printf("%s: NT_PRSTATUS not implemented; returning EINVAL\n", __func__); return (EINVAL); default: printf("%s: PTRACE_GETREGSET request %ld not implemented; " "returning EINVAL\n", __func__, addr); return (EINVAL); } } static int linux_ptrace_seize(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) { printf("%s: PTRACE_SEIZE not implemented; returning EINVAL\n", __func__); return (EINVAL); } int linux_ptrace(struct thread *td, struct linux_ptrace_args *uap) { void *addr; pid_t pid; int error, sig; pid = (pid_t)uap->pid; addr = (void *)uap->addr; switch (uap->req) { case LINUX_PTRACE_TRACEME: error = kern_ptrace(td, PT_TRACE_ME, 0, 0, 0); break; case LINUX_PTRACE_PEEKTEXT: case LINUX_PTRACE_PEEKDATA: error = linux_ptrace_peek(td, pid, addr, (void *)uap->data); if (error != 0) return (error); /* * Linux expects this syscall to read 64 bits, not 32. */ error = linux_ptrace_peek(td, pid, (void *)(uap->addr + 4), (void *)(uap->data + 4)); break; case LINUX_PTRACE_POKETEXT: error = kern_ptrace(td, PT_WRITE_I, pid, addr, uap->data); break; case LINUX_PTRACE_POKEDATA: error = kern_ptrace(td, PT_WRITE_D, pid, addr, uap->data); break; case LINUX_PTRACE_CONT: error = map_signum(uap->data, &sig); if (error != 0) break; error = kern_ptrace(td, PT_CONTINUE, pid, (void *)1, sig); break; case LINUX_PTRACE_KILL: error = kern_ptrace(td, PT_KILL, pid, addr, uap->data); break; case LINUX_PTRACE_SINGLESTEP: error = map_signum(uap->data, &sig); if (error != 0) break; error = kern_ptrace(td, PT_STEP, pid, (void *)1, sig); break; case LINUX_PTRACE_GETREGS: error = linux_ptrace_getregs(td, pid, (void *)uap->data); break; case LINUX_PTRACE_SETREGS: error = linux_ptrace_setregs(td, pid, (void *)uap->data); break; case LINUX_PTRACE_ATTACH: error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data); break; case LINUX_PTRACE_DETACH: error = map_signum(uap->data, &sig); if (error != 0) break; error = kern_ptrace(td, PT_DETACH, pid, (void *)1, sig); break; case LINUX_PTRACE_SYSCALL: error = map_signum(uap->data, &sig); if (error != 0) break; error = kern_ptrace(td, PT_SYSCALL, pid, (void *)1, sig); break; case LINUX_PTRACE_SETOPTIONS: error = linux_ptrace_setoptions(td, pid, uap->data); break; case LINUX_PTRACE_GETREGSET: error = linux_ptrace_getregset(td, pid, uap->addr, uap->data); break; case LINUX_PTRACE_SEIZE: error = linux_ptrace_seize(td, pid, uap->addr, uap->data); break; default: printf("%s: ptrace(%ld, ...) not implemented; returning EINVAL\n", __func__, uap->req); error = EINVAL; break; } return (error); } Index: stable/11/sys/dev/usb/storage/cfumass.c =================================================================== --- stable/11/sys/dev/usb/storage/cfumass.c (revision 332616) +++ stable/11/sys/dev/usb/storage/cfumass.c (revision 332617) @@ -1,998 +1,1000 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2016 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * USB Mass Storage Class Bulk-Only (BBB) Transport target. * * http://www.usb.org/developers/docs/devclass_docs/usbmassbulk_10.pdf * * This code implements the USB Mass Storage frontend driver for the CAM * Target Layer (ctl(4)) subsystem. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include "usb_if.h" #include #include #include #include #include #include #include #include #include #include #include SYSCTL_NODE(_hw_usb, OID_AUTO, cfumass, CTLFLAG_RW, 0, "CAM Target Layer USB Mass Storage Frontend"); static int debug = 1; SYSCTL_INT(_hw_usb_cfumass, OID_AUTO, debug, CTLFLAG_RWTUN, &debug, 1, "Enable debug messages"); static int max_lun = 0; SYSCTL_INT(_hw_usb_cfumass, OID_AUTO, max_lun, CTLFLAG_RWTUN, &max_lun, 1, "Maximum advertised LUN number"); static int ignore_stop = 1; SYSCTL_INT(_hw_usb_cfumass, OID_AUTO, ignore_stop, CTLFLAG_RWTUN, &ignore_stop, 1, "Ignore START STOP UNIT with START and LOEJ bits cleared"); /* * The driver uses a single, global CTL port. It could create its ports * in cfumass_attach() instead, but that would make it impossible to specify * "port cfumass0" in ctl.conf(5), as the port generally wouldn't exist * at the time ctld(8) gets run. */ struct ctl_port cfumass_port; bool cfumass_port_online; volatile u_int cfumass_refcount; #ifndef CFUMASS_BULK_SIZE #define CFUMASS_BULK_SIZE (1U << 17) /* bytes */ #endif /* * USB transfer definitions. */ #define CFUMASS_T_COMMAND 0 #define CFUMASS_T_DATA_OUT 1 #define CFUMASS_T_DATA_IN 2 #define CFUMASS_T_STATUS 3 #define CFUMASS_T_MAX 4 /* * USB interface specific control requests. */ #define UR_RESET 0xff /* Bulk-Only Mass Storage Reset */ #define UR_GET_MAX_LUN 0xfe /* Get Max LUN */ /* * Command Block Wrapper. */ struct cfumass_cbw_t { uDWord dCBWSignature; #define CBWSIGNATURE 0x43425355 /* "USBC" */ uDWord dCBWTag; uDWord dCBWDataTransferLength; uByte bCBWFlags; #define CBWFLAGS_OUT 0x00 #define CBWFLAGS_IN 0x80 uByte bCBWLUN; uByte bCDBLength; #define CBWCBLENGTH 16 uByte CBWCB[CBWCBLENGTH]; } __packed; #define CFUMASS_CBW_SIZE 31 CTASSERT(sizeof(struct cfumass_cbw_t) == CFUMASS_CBW_SIZE); /* * Command Status Wrapper. */ struct cfumass_csw_t { uDWord dCSWSignature; #define CSWSIGNATURE 0x53425355 /* "USBS" */ uDWord dCSWTag; uDWord dCSWDataResidue; uByte bCSWStatus; #define CSWSTATUS_GOOD 0x0 #define CSWSTATUS_FAILED 0x1 #define CSWSTATUS_PHASE 0x2 } __packed; #define CFUMASS_CSW_SIZE 13 CTASSERT(sizeof(struct cfumass_csw_t) == CFUMASS_CSW_SIZE); struct cfumass_softc { device_t sc_dev; struct usb_device *sc_udev; struct usb_xfer *sc_xfer[CFUMASS_T_MAX]; struct cfumass_cbw_t *sc_cbw; struct cfumass_csw_t *sc_csw; struct mtx sc_mtx; int sc_online; int sc_ctl_initid; /* * This is used to communicate between CTL callbacks * and USB callbacks; basically, it holds the state * for the current command ("the" command, since there * is no queueing in USB Mass Storage). */ bool sc_current_stalled; /* * The following are set upon receiving a SCSI command. */ int sc_current_tag; int sc_current_transfer_length; int sc_current_flags; /* * The following are set in ctl_datamove(). */ int sc_current_residue; union ctl_io *sc_ctl_io; /* * The following is set in cfumass_done(). */ int sc_current_status; /* * Number of requests queued to CTL. */ volatile u_int sc_queued; }; /* * USB interface. */ static device_probe_t cfumass_probe; static device_attach_t cfumass_attach; static device_detach_t cfumass_detach; static device_suspend_t cfumass_suspend; static device_resume_t cfumass_resume; static usb_handle_request_t cfumass_handle_request; static usb_callback_t cfumass_t_command_callback; static usb_callback_t cfumass_t_data_callback; static usb_callback_t cfumass_t_status_callback; static device_method_t cfumass_methods[] = { /* USB interface. */ DEVMETHOD(usb_handle_request, cfumass_handle_request), /* Device interface. */ DEVMETHOD(device_probe, cfumass_probe), DEVMETHOD(device_attach, cfumass_attach), DEVMETHOD(device_detach, cfumass_detach), DEVMETHOD(device_suspend, cfumass_suspend), DEVMETHOD(device_resume, cfumass_resume), DEVMETHOD_END }; static driver_t cfumass_driver = { .name = "cfumass", .methods = cfumass_methods, .size = sizeof(struct cfumass_softc), }; static devclass_t cfumass_devclass; DRIVER_MODULE(cfumass, uhub, cfumass_driver, cfumass_devclass, NULL, 0); MODULE_VERSION(cfumass, 0); MODULE_DEPEND(cfumass, usb, 1, 1, 1); MODULE_DEPEND(cfumass, usb_template, 1, 1, 1); static struct usb_config cfumass_config[CFUMASS_T_MAX] = { [CFUMASS_T_COMMAND] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = sizeof(struct cfumass_cbw_t), .callback = &cfumass_t_command_callback, .usb_mode = USB_MODE_DEVICE, }, [CFUMASS_T_DATA_OUT] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = CFUMASS_BULK_SIZE, .flags = {.proxy_buffer = 1, .short_xfer_ok = 1, .ext_buffer = 1}, .callback = &cfumass_t_data_callback, .usb_mode = USB_MODE_DEVICE, }, [CFUMASS_T_DATA_IN] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = CFUMASS_BULK_SIZE, .flags = {.proxy_buffer = 1, .short_xfer_ok = 1, .ext_buffer = 1}, .callback = &cfumass_t_data_callback, .usb_mode = USB_MODE_DEVICE, }, [CFUMASS_T_STATUS] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = sizeof(struct cfumass_csw_t), .flags = {.short_xfer_ok = 1}, .callback = &cfumass_t_status_callback, .usb_mode = USB_MODE_DEVICE, }, }; /* * CTL frontend interface. */ static int cfumass_init(void); static int cfumass_shutdown(void); static void cfumass_online(void *arg); static void cfumass_offline(void *arg); static void cfumass_datamove(union ctl_io *io); static void cfumass_done(union ctl_io *io); static struct ctl_frontend cfumass_frontend = { .name = "umass", .init = cfumass_init, .shutdown = cfumass_shutdown, }; CTL_FRONTEND_DECLARE(ctlcfumass, cfumass_frontend); #define CFUMASS_DEBUG(S, X, ...) \ do { \ if (debug > 1) { \ device_printf(S->sc_dev, "%s: " X "\n", \ __func__, ## __VA_ARGS__); \ } \ } while (0) #define CFUMASS_WARN(S, X, ...) \ do { \ if (debug > 0) { \ device_printf(S->sc_dev, "WARNING: %s: " X "\n",\ __func__, ## __VA_ARGS__); \ } \ } while (0) #define CFUMASS_LOCK(X) mtx_lock(&X->sc_mtx) #define CFUMASS_UNLOCK(X) mtx_unlock(&X->sc_mtx) static void cfumass_transfer_start(struct cfumass_softc *sc, uint8_t xfer_index); static void cfumass_terminate(struct cfumass_softc *sc); static int cfumass_probe(device_t dev) { struct usb_attach_arg *uaa; struct usb_interface_descriptor *id; uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_DEVICE) return (ENXIO); /* * Check for a compliant device. */ id = usbd_get_interface_descriptor(uaa->iface); if ((id == NULL) || (id->bInterfaceClass != UICLASS_MASS) || (id->bInterfaceSubClass != UISUBCLASS_SCSI) || (id->bInterfaceProtocol != UIPROTO_MASS_BBB)) { return (ENXIO); } return (BUS_PROBE_GENERIC); } static int cfumass_attach(device_t dev) { struct cfumass_softc *sc; struct usb_attach_arg *uaa; int error; sc = device_get_softc(dev); uaa = device_get_ivars(dev); sc->sc_dev = dev; sc->sc_udev = uaa->device; CFUMASS_DEBUG(sc, "go"); usbd_set_power_mode(uaa->device, USB_POWER_MODE_SAVE); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "cfumass", NULL, MTX_DEF); refcount_acquire(&cfumass_refcount); error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, cfumass_config, CFUMASS_T_MAX, sc, &sc->sc_mtx); if (error != 0) { CFUMASS_WARN(sc, "usbd_transfer_setup() failed: %s", usbd_errstr(error)); refcount_release(&cfumass_refcount); return (ENXIO); } sc->sc_cbw = usbd_xfer_get_frame_buffer(sc->sc_xfer[CFUMASS_T_COMMAND], 0); sc->sc_csw = usbd_xfer_get_frame_buffer(sc->sc_xfer[CFUMASS_T_STATUS], 0); sc->sc_ctl_initid = ctl_add_initiator(&cfumass_port, -1, 0, NULL); if (sc->sc_ctl_initid < 0) { CFUMASS_WARN(sc, "ctl_add_initiator() failed with error %d", sc->sc_ctl_initid); usbd_transfer_unsetup(sc->sc_xfer, CFUMASS_T_MAX); refcount_release(&cfumass_refcount); return (ENXIO); } refcount_init(&sc->sc_queued, 0); CFUMASS_LOCK(sc); cfumass_transfer_start(sc, CFUMASS_T_COMMAND); CFUMASS_UNLOCK(sc); return (0); } static int cfumass_detach(device_t dev) { struct cfumass_softc *sc; int error; sc = device_get_softc(dev); CFUMASS_DEBUG(sc, "go"); CFUMASS_LOCK(sc); cfumass_terminate(sc); CFUMASS_UNLOCK(sc); usbd_transfer_unsetup(sc->sc_xfer, CFUMASS_T_MAX); if (sc->sc_ctl_initid != -1) { error = ctl_remove_initiator(&cfumass_port, sc->sc_ctl_initid); if (error != 0) { CFUMASS_WARN(sc, "ctl_remove_initiator() failed " "with error %d", error); } sc->sc_ctl_initid = -1; } mtx_destroy(&sc->sc_mtx); refcount_release(&cfumass_refcount); return (0); } static int cfumass_suspend(device_t dev) { struct cfumass_softc *sc; sc = device_get_softc(dev); CFUMASS_DEBUG(sc, "go"); return (0); } static int cfumass_resume(device_t dev) { struct cfumass_softc *sc; sc = device_get_softc(dev); CFUMASS_DEBUG(sc, "go"); return (0); } static void cfumass_transfer_start(struct cfumass_softc *sc, uint8_t xfer_index) { usbd_transfer_start(sc->sc_xfer[xfer_index]); } static void cfumass_transfer_stop_and_drain(struct cfumass_softc *sc, uint8_t xfer_index) { usbd_transfer_stop(sc->sc_xfer[xfer_index]); CFUMASS_UNLOCK(sc); usbd_transfer_drain(sc->sc_xfer[xfer_index]); CFUMASS_LOCK(sc); } static void cfumass_terminate(struct cfumass_softc *sc) { int last; for (;;) { cfumass_transfer_stop_and_drain(sc, CFUMASS_T_COMMAND); cfumass_transfer_stop_and_drain(sc, CFUMASS_T_DATA_IN); cfumass_transfer_stop_and_drain(sc, CFUMASS_T_DATA_OUT); if (sc->sc_ctl_io != NULL) { CFUMASS_DEBUG(sc, "terminating CTL transfer"); ctl_set_data_phase_error(&sc->sc_ctl_io->scsiio); sc->sc_ctl_io->scsiio.be_move_done(sc->sc_ctl_io); sc->sc_ctl_io = NULL; } cfumass_transfer_stop_and_drain(sc, CFUMASS_T_STATUS); refcount_acquire(&sc->sc_queued); last = refcount_release(&sc->sc_queued); if (last != 0) break; CFUMASS_DEBUG(sc, "%d CTL tasks pending", sc->sc_queued); msleep(__DEVOLATILE(void *, &sc->sc_queued), &sc->sc_mtx, 0, "cfumass_reset", hz / 100); } } static int cfumass_handle_request(device_t dev, const void *preq, void **pptr, uint16_t *plen, uint16_t offset, uint8_t *pstate) { static uint8_t max_lun_tmp; struct cfumass_softc *sc; const struct usb_device_request *req; uint8_t is_complete; sc = device_get_softc(dev); req = preq; is_complete = *pstate; CFUMASS_DEBUG(sc, "go"); if (is_complete) return (ENXIO); if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && (req->bRequest == UR_RESET)) { CFUMASS_WARN(sc, "received Bulk-Only Mass Storage Reset"); *plen = 0; CFUMASS_LOCK(sc); cfumass_terminate(sc); cfumass_transfer_start(sc, CFUMASS_T_COMMAND); CFUMASS_UNLOCK(sc); CFUMASS_DEBUG(sc, "Bulk-Only Mass Storage Reset done"); return (0); } if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) && (req->bRequest == UR_GET_MAX_LUN)) { CFUMASS_DEBUG(sc, "received Get Max LUN"); if (offset == 0) { *plen = 1; /* * The protocol doesn't support LUN numbers higher * than 15. Also, some initiators (namely Windows XP * SP3 Version 2002) can't properly query the number * of LUNs, resulting in inaccessible "fake" ones - thus * the default limit of one LUN. */ if (max_lun < 0 || max_lun > 15) { CFUMASS_WARN(sc, "invalid hw.usb.cfumass.max_lun, must be " "between 0 and 15; defaulting to 0"); max_lun_tmp = 0; } else { max_lun_tmp = max_lun; } *pptr = &max_lun_tmp; } else { *plen = 0; } return (0); } return (ENXIO); } static int cfumass_quirk(struct cfumass_softc *sc, unsigned char *cdb, int cdb_len) { struct scsi_start_stop_unit *sssu; switch (cdb[0]) { case START_STOP_UNIT: /* * Some initiators - eg OSX, Darwin Kernel Version 15.6.0, * root:xnu-3248.60.11~2/RELEASE_X86_64 - attempt to stop * the unit on eject, but fail to start it when it's plugged * back. Just ignore the command. */ if (cdb_len < sizeof(*sssu)) { CFUMASS_DEBUG(sc, "received START STOP UNIT with " "bCDBLength %d, should be %zd", cdb_len, sizeof(*sssu)); break; } sssu = (struct scsi_start_stop_unit *)cdb; if ((sssu->how & SSS_PC_MASK) != 0) break; if ((sssu->how & SSS_START) != 0) break; if ((sssu->how & SSS_LOEJ) != 0) break; if (ignore_stop == 0) { break; } else if (ignore_stop == 1) { CFUMASS_WARN(sc, "ignoring START STOP UNIT request"); } else { CFUMASS_DEBUG(sc, "ignoring START STOP UNIT request"); } sc->sc_current_status = 0; cfumass_transfer_start(sc, CFUMASS_T_STATUS); return (1); default: break; } return (0); } static void cfumass_t_command_callback(struct usb_xfer *xfer, usb_error_t usb_error) { struct cfumass_softc *sc; uint32_t signature; union ctl_io *io; int error = 0; sc = usbd_xfer_softc(xfer); KASSERT(sc->sc_ctl_io == NULL, ("sc_ctl_io is %p, should be NULL", sc->sc_ctl_io)); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: CFUMASS_DEBUG(sc, "USB_ST_TRANSFERRED"); signature = UGETDW(sc->sc_cbw->dCBWSignature); if (signature != CBWSIGNATURE) { CFUMASS_WARN(sc, "wrong dCBWSignature 0x%08x, " "should be 0x%08x", signature, CBWSIGNATURE); break; } if (sc->sc_cbw->bCDBLength <= 0 || sc->sc_cbw->bCDBLength > sizeof(sc->sc_cbw->CBWCB)) { CFUMASS_WARN(sc, "invalid bCDBLength %d, should be <= %zd", sc->sc_cbw->bCDBLength, sizeof(sc->sc_cbw->CBWCB)); break; } sc->sc_current_stalled = false; sc->sc_current_status = 0; sc->sc_current_tag = UGETDW(sc->sc_cbw->dCBWTag); sc->sc_current_transfer_length = UGETDW(sc->sc_cbw->dCBWDataTransferLength); sc->sc_current_flags = sc->sc_cbw->bCBWFlags; /* * Make sure to report proper residue if the datamove wasn't * required, or wasn't called due to SCSI error. */ sc->sc_current_residue = sc->sc_current_transfer_length; if (cfumass_quirk(sc, sc->sc_cbw->CBWCB, sc->sc_cbw->bCDBLength) != 0) break; if (!cfumass_port_online) { CFUMASS_DEBUG(sc, "cfumass port is offline; stalling"); usbd_xfer_set_stall(xfer); break; } /* * Those CTL functions cannot be called with mutex held. */ CFUMASS_UNLOCK(sc); io = ctl_alloc_io(cfumass_port.ctl_pool_ref); ctl_zero_io(io); io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = sc; io->io_hdr.io_type = CTL_IO_SCSI; io->io_hdr.nexus.initid = sc->sc_ctl_initid; io->io_hdr.nexus.targ_port = cfumass_port.targ_port; io->io_hdr.nexus.targ_lun = ctl_decode_lun(sc->sc_cbw->bCBWLUN); io->scsiio.tag_num = UGETDW(sc->sc_cbw->dCBWTag); io->scsiio.tag_type = CTL_TAG_UNTAGGED; io->scsiio.cdb_len = sc->sc_cbw->bCDBLength; memcpy(io->scsiio.cdb, sc->sc_cbw->CBWCB, sc->sc_cbw->bCDBLength); refcount_acquire(&sc->sc_queued); error = ctl_queue(io); if (error != CTL_RETVAL_COMPLETE) { CFUMASS_WARN(sc, "ctl_queue() failed; error %d; stalling", error); ctl_free_io(io); refcount_release(&sc->sc_queued); CFUMASS_LOCK(sc); usbd_xfer_set_stall(xfer); break; } CFUMASS_LOCK(sc); break; case USB_ST_SETUP: tr_setup: CFUMASS_DEBUG(sc, "USB_ST_SETUP"); usbd_xfer_set_frame_len(xfer, 0, sizeof(*sc->sc_cbw)); usbd_transfer_submit(xfer); break; default: if (usb_error == USB_ERR_CANCELLED) { CFUMASS_DEBUG(sc, "USB_ERR_CANCELLED"); break; } CFUMASS_DEBUG(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error)); goto tr_setup; } } static void cfumass_t_data_callback(struct usb_xfer *xfer, usb_error_t usb_error) { struct cfumass_softc *sc = usbd_xfer_softc(xfer); union ctl_io *io = sc->sc_ctl_io; uint32_t max_bulk; struct ctl_sg_entry sg_entry, *sglist; int actlen, sumlen, sg_count; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: CFUMASS_DEBUG(sc, "USB_ST_TRANSFERRED"); usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); sc->sc_current_residue -= actlen; io->scsiio.ext_data_filled += actlen; io->scsiio.kern_data_resid -= actlen; if (actlen < sumlen || sc->sc_current_residue == 0 || io->scsiio.kern_data_resid == 0) { sc->sc_ctl_io = NULL; io->scsiio.be_move_done(io); break; } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: CFUMASS_DEBUG(sc, "USB_ST_SETUP"); if (io->scsiio.kern_sg_entries > 0) { sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr; sg_count = io->scsiio.kern_sg_entries; } else { sglist = &sg_entry; sglist->addr = io->scsiio.kern_data_ptr; sglist->len = io->scsiio.kern_data_len; sg_count = 1; } sumlen = io->scsiio.ext_data_filled - io->scsiio.kern_rel_offset; while (sumlen >= sglist->len && sg_count > 0) { sumlen -= sglist->len; sglist++; sg_count--; } KASSERT(sg_count > 0, ("Run out of S/G list entries")); max_bulk = usbd_xfer_max_len(xfer); actlen = min(sglist->len - sumlen, max_bulk); actlen = min(actlen, sc->sc_current_transfer_length - io->scsiio.ext_data_filled); CFUMASS_DEBUG(sc, "requested %d, done %d, max_bulk %d, " "segment %zd => transfer %d", sc->sc_current_transfer_length, io->scsiio.ext_data_filled, max_bulk, sglist->len - sumlen, actlen); usbd_xfer_set_frame_data(xfer, 0, (uint8_t *)sglist->addr + sumlen, actlen); usbd_transfer_submit(xfer); break; default: if (usb_error == USB_ERR_CANCELLED) { CFUMASS_DEBUG(sc, "USB_ERR_CANCELLED"); break; } CFUMASS_DEBUG(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error)); goto tr_setup; } } static void cfumass_t_status_callback(struct usb_xfer *xfer, usb_error_t usb_error) { struct cfumass_softc *sc; sc = usbd_xfer_softc(xfer); KASSERT(sc->sc_ctl_io == NULL, ("sc_ctl_io is %p, should be NULL", sc->sc_ctl_io)); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: CFUMASS_DEBUG(sc, "USB_ST_TRANSFERRED"); cfumass_transfer_start(sc, CFUMASS_T_COMMAND); break; case USB_ST_SETUP: tr_setup: CFUMASS_DEBUG(sc, "USB_ST_SETUP"); if (sc->sc_current_residue > 0 && !sc->sc_current_stalled) { CFUMASS_DEBUG(sc, "non-zero residue, stalling"); usbd_xfer_set_stall(xfer); sc->sc_current_stalled = true; } USETDW(sc->sc_csw->dCSWSignature, CSWSIGNATURE); USETDW(sc->sc_csw->dCSWTag, sc->sc_current_tag); USETDW(sc->sc_csw->dCSWDataResidue, sc->sc_current_residue); sc->sc_csw->bCSWStatus = sc->sc_current_status; usbd_xfer_set_frame_len(xfer, 0, sizeof(*sc->sc_csw)); usbd_transfer_submit(xfer); break; default: if (usb_error == USB_ERR_CANCELLED) { CFUMASS_DEBUG(sc, "USB_ERR_CANCELLED"); break; } CFUMASS_DEBUG(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error)); goto tr_setup; } } static void cfumass_online(void *arg __unused) { cfumass_port_online = true; } static void cfumass_offline(void *arg __unused) { cfumass_port_online = false; } static void cfumass_datamove(union ctl_io *io) { struct cfumass_softc *sc; sc = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; CFUMASS_DEBUG(sc, "go"); CFUMASS_LOCK(sc); KASSERT(sc->sc_ctl_io == NULL, ("sc_ctl_io is %p, should be NULL", sc->sc_ctl_io)); sc->sc_ctl_io = io; if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN) { /* * Verify that CTL wants us to send the data in the direction * expected by the initiator. */ if (sc->sc_current_flags != CBWFLAGS_IN) { CFUMASS_WARN(sc, "wrong bCBWFlags 0x%x, should be 0x%x", sc->sc_current_flags, CBWFLAGS_IN); goto fail; } cfumass_transfer_start(sc, CFUMASS_T_DATA_IN); } else { if (sc->sc_current_flags != CBWFLAGS_OUT) { CFUMASS_WARN(sc, "wrong bCBWFlags 0x%x, should be 0x%x", sc->sc_current_flags, CBWFLAGS_OUT); goto fail; } cfumass_transfer_start(sc, CFUMASS_T_DATA_OUT); } CFUMASS_UNLOCK(sc); return; fail: ctl_set_data_phase_error(&io->scsiio); io->scsiio.be_move_done(io); sc->sc_ctl_io = NULL; } static void cfumass_done(union ctl_io *io) { struct cfumass_softc *sc; sc = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr; CFUMASS_DEBUG(sc, "go"); KASSERT(((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE), ("invalid CTL status %#x", io->io_hdr.status)); KASSERT(sc->sc_ctl_io == NULL, ("sc_ctl_io is %p, should be NULL", sc->sc_ctl_io)); if (io->io_hdr.io_type == CTL_IO_TASK && io->taskio.task_action == CTL_TASK_I_T_NEXUS_RESET) { /* * Implicit task termination has just completed; nothing to do. */ ctl_free_io(io); return; } /* * Do not return status for aborted commands. * There are exceptions, but none supported by CTL yet. */ if (((io->io_hdr.flags & CTL_FLAG_ABORT) && (io->io_hdr.flags & CTL_FLAG_ABORT_STATUS) == 0) || (io->io_hdr.flags & CTL_FLAG_STATUS_SENT)) { ctl_free_io(io); return; } if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) sc->sc_current_status = 0; else sc->sc_current_status = 1; /* XXX: How should we report BUSY, RESERVATION CONFLICT, etc? */ if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SCSI_ERROR && io->scsiio.scsi_status == SCSI_STATUS_CHECK_COND) ctl_queue_sense(io); else ctl_free_io(io); CFUMASS_LOCK(sc); cfumass_transfer_start(sc, CFUMASS_T_STATUS); CFUMASS_UNLOCK(sc); refcount_release(&sc->sc_queued); } int cfumass_init(void) { int error; cfumass_port.frontend = &cfumass_frontend; cfumass_port.port_type = CTL_PORT_UMASS; cfumass_port.num_requested_ctl_io = 1; cfumass_port.port_name = "cfumass"; cfumass_port.physical_port = 0; cfumass_port.virtual_port = 0; cfumass_port.port_online = cfumass_online; cfumass_port.port_offline = cfumass_offline; cfumass_port.onoff_arg = NULL; cfumass_port.fe_datamove = cfumass_datamove; cfumass_port.fe_done = cfumass_done; cfumass_port.targ_port = -1; error = ctl_port_register(&cfumass_port); if (error != 0) { printf("%s: ctl_port_register() failed " "with error %d", __func__, error); } cfumass_port_online = true; refcount_init(&cfumass_refcount, 0); return (error); } int cfumass_shutdown(void) { int error; if (cfumass_refcount > 0) { if (debug > 1) { printf("%s: still have %u attachments; " "returning EBUSY\n", __func__, cfumass_refcount); } return (EBUSY); } error = ctl_port_deregister(&cfumass_port); if (error != 0) { printf("%s: ctl_port_deregister() failed " "with error %d\n", __func__, error); } return (error); } Index: stable/11 =================================================================== --- stable/11 (revision 332616) +++ stable/11 (revision 332617) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r328342