diff --git a/sys/dev/mrsas/mrsas.h b/sys/dev/mrsas/mrsas.h --- a/sys/dev/mrsas/mrsas.h +++ b/sys/dev/mrsas/mrsas.h @@ -3648,4 +3648,7 @@ return ((volatile int *)p)[b >> 5] & (1 << (b & 0x1f)); } +#include "mrsas_ioctl.h" +extern int mrsas_user_command(struct mrsas_softc *, struct mfi_ioc_passthru *); + #endif /* MRSAS_H */ diff --git a/sys/dev/mrsas/mrsas.c b/sys/dev/mrsas/mrsas.c --- a/sys/dev/mrsas/mrsas.c +++ b/sys/dev/mrsas/mrsas.c @@ -1449,7 +1449,14 @@ int ret = 0, i = 0; MRSAS_DRV_PCI_INFORMATION *pciDrvInfo; - sc = mrsas_get_softc_instance(dev, cmd, arg); + switch (cmd) { + case MFIIO_PASSTHRU: + sc = (struct mrsas_softc *)(dev->si_drv1); + break; + default: + sc = mrsas_get_softc_instance(dev, cmd, arg); + break; + } if (!sc) return ENOENT; @@ -1512,6 +1519,10 @@ ret = 0; break; + case MFIIO_PASSTHRU: + ret = mrsas_user_command(sc, (struct mfi_ioc_passthru *)arg); + break; + default: mrsas_dprint(sc, MRSAS_TRACE, "IOCTL command 0x%lx is not handled\n", cmd); ret = ENOENT; diff --git a/sys/dev/mrsas/mrsas_ioctl.h b/sys/dev/mrsas/mrsas_ioctl.h --- a/sys/dev/mrsas/mrsas_ioctl.h +++ b/sys/dev/mrsas/mrsas_ioctl.h @@ -121,4 +121,13 @@ #pragma pack() #endif /* COMPAT_FREEBSD32 */ +struct mfi_ioc_passthru { + struct mrsas_dcmd_frame ioc_frame; + uint32_t pad_skinny_flag; + uint32_t buf_size; + uint8_t *buf; +} __packed; + +#define MFIIO_PASSTHRU _IOWR('C', 102, struct mfi_ioc_passthru) + #endif /* MRSAS_IOCTL_H */ diff --git a/sys/dev/mrsas/mrsas_ioctl.c b/sys/dev/mrsas/mrsas_ioctl.c --- a/sys/dev/mrsas/mrsas_ioctl.c +++ b/sys/dev/mrsas/mrsas_ioctl.c @@ -43,6 +43,18 @@ #include #include +struct mrsas_passthru_cmd { + struct iovec *kern_sge; + struct mrsas_softc *sc; + struct mrsas_mfi_cmd *cmd; + bus_dma_tag_t ioctl_data_tag; + bus_dmamap_t ioctl_data_dmamap; + + u_int32_t error_code; + u_int32_t sge_count; + int complete; +}; + /* * Function prototypes */ @@ -62,6 +74,54 @@ mrsas_issue_blocked_cmd(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd); +/* + * mrsas_data_load_cb: Callback entry point + * input: Pointer to command packet as argument + * Pointer to segment + * Number of segments Error + * + * This is the callback function of the bus dma map load. It builds the SG + * list. + */ +static void +mrsas_passthru_load_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct mrsas_passthru_cmd *cb = (struct mrsas_passthru_cmd *)arg; + struct mrsas_softc *sc = cb->sc; + int i = 0; + + if (error) { + cb->error_code = error; + if (error == EFBIG) { + device_printf(sc->mrsas_dev, "mrsas_passthru_load_cb: " + "error=%d EFBIG\n", error); + cb->complete = 1; + return; + } else { + device_printf(sc->mrsas_dev, "mrsas_passthru_load_cb: " + "error=%d UNKNOWN\n", error); + } + } + if (nseg > MAX_IOCTL_SGE) { + cb->error_code = EFBIG; + device_printf(sc->mrsas_dev, "mrsas_passthru_load_cb: " + "too many segments: %d\n", nseg); + cb->complete = 1; + return; + } + + for (i = 0; i < nseg; i++) { + cb->kern_sge[i].iov_base = PTRIN(segs[i].ds_addr); + cb->kern_sge[i].iov_len = segs[i].ds_len; + } + cb->sge_count = nseg; + + bus_dmamap_sync(cb->ioctl_data_tag, cb->ioctl_data_dmamap, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + cb->complete = 1; +} + /* * mrsas_passthru: Handle pass-through commands * input: Adapter instance soft state argument pointer @@ -344,6 +404,200 @@ return (ret); } +/** + * mrsas_user_command: Handle user mode DCMD and buffer + * input: Adapter instance soft state + * argument pointer + * + * This function is called from mrsas_ioctl() DCMDs to firmware for mfiutil + */ +int +mrsas_user_command(struct mrsas_softc *sc, struct mfi_ioc_passthru *ioc) +{ + struct mrsas_mfi_cmd *cmd; + struct mrsas_dcmd_frame *dcmd; + struct mrsas_passthru_cmd *passcmd; + bus_dma_tag_t ioctl_data_tag; + bus_dmamap_t ioctl_data_dmamap; + bus_addr_t ioctl_data_phys_addr; + struct iovec *kern_sge; + int ret, ioctl_data_size; + char *ioctl_temp_data_mem; + + ret = 0; + ioctl_temp_data_mem = NULL; + passcmd = NULL; + ioctl_data_phys_addr = 0; + dcmd = NULL; + cmd = NULL; + ioctl_data_tag = NULL; + ioctl_data_dmamap = NULL; + ioctl_data_dmamap = NULL; + + /* Get a command */ + cmd = mrsas_get_mfi_cmd(sc); + if (!cmd) { + device_printf(sc->mrsas_dev, + "Failed to get a free cmd for IOCTL\n"); + return(ENOMEM); + } + + /* + * Frame is DCMD + */ + dcmd = (struct mrsas_dcmd_frame *)cmd->frame; + memcpy(dcmd, &ioc->ioc_frame, sizeof(struct mrsas_dcmd_frame)); + + ioctl_data_size = ioc->buf_size; + + cmd->frame->hdr.context = cmd->index; + cmd->frame->hdr.pad_0 = 0; + cmd->frame->hdr.flags = MFI_FRAME_DIR_BOTH; + if (sizeof(bus_addr_t) == 8) + cmd->frame->hdr.flags |= MFI_FRAME_SGL64 | MFI_FRAME_SENSE64; + + kern_sge = (struct iovec *)(&dcmd->sgl); + + if (ioctl_data_size == 0) { + kern_sge[0].iov_base = 0; + kern_sge[0].iov_len = 0; + } else { + ioctl_temp_data_mem = malloc(ioc->buf_size, M_MRSAS, M_WAITOK); + if (ioctl_temp_data_mem == NULL) { + device_printf(sc->mrsas_dev, "Could not allocate " + "%d memory for temporary passthrough ioctl\n", + ioc->buf_size); + ret = ENOMEM; + goto out; + } + + /* Copy in data from user space */ + ret = copyin(ioc->buf, ioctl_temp_data_mem, ioc->buf_size); + if (ret) { + device_printf(sc->mrsas_dev, "IOCTL copyin failed!\n"); + goto out; + } + + /* + * Allocate a temporary struct to hold parameters for the + * callback + */ + passcmd = malloc(sizeof(struct mrsas_passthru_cmd), M_MRSAS, + M_WAITOK); + if (passcmd == NULL) { + device_printf(sc->mrsas_dev, "Could not allocate " + "memory for temporary passthrough cb struct\n"); + ret = ENOMEM; + goto out; + } + passcmd->complete = 0; + passcmd->sc = sc; + passcmd->cmd = cmd; + passcmd->kern_sge = kern_sge; + + /* + * Create a dma tag for passthru buffers + */ + if (bus_dma_tag_create(sc->mrsas_parent_tag, /* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + ioctl_data_size, /* maxsize */ + MAX_IOCTL_SGE, /* msegments */ + ioctl_data_size, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + busdma_lock_mutex, /* lockfunc */ + &sc->ioctl_lock, /* lockarg */ + &ioctl_data_tag)) { + device_printf(sc->mrsas_dev, + "Cannot allocate ioctl data tag %d\n", + ioc->buf_size); + ret = ENOMEM; + goto out; + } + + /* Create memmap */ + if (bus_dmamap_create(ioctl_data_tag, 0, &ioctl_data_dmamap)) { + device_printf(sc->mrsas_dev, "Cannot create ioctl " + "passthru dmamap\n"); + ret = ENOMEM; + goto out; + } + + passcmd->ioctl_data_tag = ioctl_data_tag; + passcmd->ioctl_data_dmamap = ioctl_data_dmamap; + + /* Map data buffer into bus space */ + if (bus_dmamap_load(ioctl_data_tag, ioctl_data_dmamap, + ioctl_temp_data_mem, ioc->buf_size, mrsas_passthru_load_cb, + passcmd, BUS_DMA_NOWAIT)) { + device_printf(sc->mrsas_dev, "Cannot load ioctl " + "passthru data mem%s %d\n", curproc->p_comm, ioctl_data_size); + ret = ENOMEM; + goto out; + } + + while (passcmd->complete == 0) { + pause("mrsas_passthru", hz); + } + + cmd->frame->dcmd.sge_count = passcmd->sge_count; + } + + /* + * Set the sync_cmd flag so that the ISR knows not to complete this + * cmd to the SCSI mid-layer + */ + cmd->sync_cmd = 1; + mrsas_issue_blocked_cmd(sc, cmd); + cmd->sync_cmd = 0; + + if (ioctl_data_size != 0) { + bus_dmamap_sync(ioctl_data_tag, ioctl_data_dmamap, + BUS_DMASYNC_POSTREAD); + /* + * copy out the kernel buffers to user buffers + */ + ret = copyout(ioctl_temp_data_mem, ioc->buf, ioc->buf_size); + if (ret) { + device_printf(sc->mrsas_dev, + "IOCTL copyout failed!\n"); + goto out; + } + } + + /* + * Return command status to user space + */ + memcpy(&ioc->ioc_frame.cmd_status, &cmd->frame->hdr.cmd_status, + sizeof(u_int8_t)); + +out: + /* + * Release temporary passthrough ioctl + */ + if (ioctl_temp_data_mem) + free(ioctl_temp_data_mem, M_MRSAS); + if (passcmd) + free(passcmd, M_MRSAS); + + /* + * Release data buffers + */ + if (ioctl_data_phys_addr) { + bus_dmamap_unload(ioctl_data_tag, ioctl_data_dmamap); + bus_dmamap_destroy(ioctl_data_tag, ioctl_data_dmamap); + } + if (ioctl_data_tag != NULL) + bus_dma_tag_destroy(ioctl_data_tag); + /* Free command */ + mrsas_release_mfi_cmd(cmd); + + return(ret); +} + + /* * mrsas_alloc_mfi_cmds: Allocates the command packets * input: Adapter instance soft state