/** * mrsas_passthru: Handle pass-through commands * input: Adapter instance soft state * argument pointer * * This function is called from mrsas_ioctl() to handle pass-through and * ioctl commands to Firmware. */ int mrsas_passthru( struct mrsas_softc *sc, void *arg ) { struct mrsas_iocpacket *user_ioc = (struct mrsas_iocpacket *)arg; union mrsas_frame *in_cmd = (union mrsas_frame *) &(user_ioc->frame.raw); struct mrsas_mfi_cmd *cmd = NULL; bus_dma_tag_t ioctl_data_tag[MAX_IOCTL_SGE]; bus_dmamap_t ioctl_data_dmamap[MAX_IOCTL_SGE]; void *ioctl_data_mem[MAX_IOCTL_SGE]; // ioctl data virtual addr bus_addr_t ioctl_data_phys_addr[MAX_IOCTL_SGE]; // ioctl data phys addr bus_dma_tag_t ioctl_sense_tag = 0; bus_dmamap_t ioctl_sense_dmamap = 0; void *ioctl_sense_mem = NULL; bus_addr_t ioctl_sense_phys_addr = 0; int i, adapter, ioctl_data_size, ioctl_sense_size, ret=0; struct mrsas_sge32 *kern_sge32; unsigned long *sense_ptr; /* For debug - uncomment the following line for debug output */ //mrsas_dump_ioctl(sc, user_ioc); /* * Check for NOP from MegaCli... MegaCli can issue a DCMD of 0. In this * case do nothing and return 0 to it as status. */ if (in_cmd->dcmd.opcode == 0) { device_printf(sc->mrsas_dev, "In %s() Got a NOP\n", __func__); user_ioc->frame.hdr.cmd_status = MFI_STAT_OK; return (0); } /* Validate host_no */ adapter = user_ioc->host_no; if (adapter != device_get_unit(sc->mrsas_dev)) { device_printf(sc->mrsas_dev, "In %s() IOCTL not for me!\n", __func__); return(ENOENT); } /* Validate SGL length */ if (user_ioc->sge_count > MAX_IOCTL_SGE) { device_printf(sc->mrsas_dev, "In %s() SGL is too long (%d > 8).\n", __func__, user_ioc->sge_count); return(ENOENT); } /* 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); } /* * User's IOCTL packet has 2 frames (maximum). Copy those two * frames into our cmd's frames. cmd->frame's context will get * overwritten when we copy from user's frames. So set that value * alone separately */ memcpy(cmd->frame, user_ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE); cmd->frame->hdr.context = cmd->index; cmd->frame->hdr.pad_0 = 0; cmd->frame->hdr.flags &= ~(MFI_FRAME_IEEE | MFI_FRAME_SGL64 | MFI_FRAME_SENSE64); /* * The management interface between applications and the fw uses * MFI frames. E.g, RAID configuration changes, LD property changes * etc are accomplishes through different kinds of MFI frames. The * driver needs to care only about substituting user buffers with * kernel buffers in SGLs. The location of SGL is embedded in the * struct iocpacket itself. */ kern_sge32 = (struct mrsas_sge32 *) ((unsigned long)cmd->frame + user_ioc->sgl_off); /* * For each user buffer, create a mirror buffer and copy in */ for (i=0; i < user_ioc->sge_count; i++) { if (!user_ioc->sgl[i].iov_len) continue; ioctl_data_size = user_ioc->sgl[i].iov_len; if (bus_dma_tag_create( sc->mrsas_parent_tag, // parent 1, 0, // algnmnt, boundary BUS_SPACE_MAXADDR_32BIT,// lowaddr BUS_SPACE_MAXADDR, // highaddr NULL, NULL, // filter, filterarg ioctl_data_size, // maxsize 1, // msegments ioctl_data_size, // maxsegsize BUS_DMA_ALLOCNOW, // flags &ioctl_data_tag[i])) { device_printf(sc->mrsas_dev, "Cannot allocate ioctl data tag\n"); return (ENOMEM); } if (bus_dmamem_alloc(ioctl_data_tag[i], (void **)&ioctl_data_mem[i], (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_data_dmamap[i])) { device_printf(sc->mrsas_dev, "Cannot allocate ioctl data mem\n"); return (ENOMEM); } if (bus_dmamap_load(ioctl_data_tag[i], ioctl_data_dmamap[i], ioctl_data_mem[i], ioctl_data_size, mrsas_alloc_cb, &ioctl_data_phys_addr[i], BUS_DMA_NOWAIT)) { device_printf(sc->mrsas_dev, "Cannot load ioctl data mem\n"); return (ENOMEM); } /* Save the physical address and length */ kern_sge32[i].phys_addr = (u_int32_t)ioctl_data_phys_addr[i]; kern_sge32[i].length = user_ioc->sgl[i].iov_len; /* Copy in data from user space */ ret = copyin(user_ioc->sgl[i].iov_base, ioctl_data_mem[i], user_ioc->sgl[i].iov_len); if (ret) { device_printf(sc->mrsas_dev, "IOCTL copyin failed!\n"); goto out; } } ioctl_sense_size = user_ioc->sense_len; if (user_ioc->sense_len) { if (bus_dma_tag_create( sc->mrsas_parent_tag, // parent 1, 0, // algnmnt, boundary BUS_SPACE_MAXADDR_32BIT,// lowaddr BUS_SPACE_MAXADDR, // highaddr NULL, NULL, // filter, filterarg ioctl_sense_size, // maxsize 1, // msegments ioctl_sense_size, // maxsegsize BUS_DMA_ALLOCNOW, // flags &ioctl_sense_tag)) { device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense tag\n"); return (ENOMEM); } if (bus_dmamem_alloc(ioctl_sense_tag, (void **)&ioctl_sense_mem, (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_sense_dmamap)) { device_printf(sc->mrsas_dev, "Cannot allocate ioctl data mem\n"); return (ENOMEM); } if (bus_dmamap_load(ioctl_sense_tag, ioctl_sense_dmamap, ioctl_sense_mem, ioctl_sense_size, mrsas_alloc_cb, &ioctl_sense_phys_addr, BUS_DMA_NOWAIT)) { device_printf(sc->mrsas_dev, "Cannot load ioctl sense mem\n"); return (ENOMEM); } sense_ptr = (unsigned long *)((unsigned long)cmd->frame + user_ioc->sense_off); sense_ptr = ioctl_sense_mem; } /* * 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; /* * copy out the kernel buffers to user buffers */ for (i = 0; i < user_ioc->sge_count; i++) { ret = copyout(ioctl_data_mem[i], user_ioc->sgl[i].iov_base, user_ioc->sgl[i].iov_len); if (ret) { device_printf(sc->mrsas_dev, "IOCTL copyout failed!\n"); goto out; } } /* * copy out the sense */ if (user_ioc->sense_len) { /* * sense_buff points to the location that has the user * sense buffer address */ sense_ptr = (unsigned long *) ((unsigned long)user_ioc->frame.raw + user_ioc->sense_off); ret = copyout(ioctl_sense_mem, (unsigned long*)*sense_ptr, user_ioc->sense_len); if (ret) { device_printf(sc->mrsas_dev, "IOCTL sense copyout failed!\n"); goto out; } } /* * Return command status to user space */ memcpy(&user_ioc->frame.hdr.cmd_status, &cmd->frame->hdr.cmd_status, sizeof(u_int8_t)); out: /* * Release sense buffer */ if (ioctl_sense_phys_addr) bus_dmamap_unload(ioctl_sense_tag, ioctl_sense_dmamap); if (ioctl_sense_mem) bus_dmamem_free(ioctl_sense_tag, ioctl_sense_mem, ioctl_sense_dmamap); if (ioctl_sense_tag) bus_dma_tag_destroy(ioctl_sense_tag); /* * Release data buffers */ for (i = 0; i < user_ioc->sge_count; i++) { if (!user_ioc->sgl[i].iov_len) continue; if (ioctl_data_phys_addr[i]) bus_dmamap_unload(ioctl_data_tag[i], ioctl_data_dmamap[i]); if (ioctl_data_mem[i] != NULL) bus_dmamem_free(ioctl_data_tag[i], ioctl_data_mem[i], ioctl_data_dmamap[i]); if (ioctl_data_tag[i] != NULL) bus_dma_tag_destroy(ioctl_data_tag[i]); } /* Free command */ mrsas_release_mfi_cmd(cmd); return(ret); }
/* * mrsas_passthru: Handle pass-through commands * input: Adapter instance soft state argument pointer * * This function is called from mrsas_ioctl() to handle pass-through and ioctl * commands to Firmware. */ int mrsas_passthru(struct mrsas_softc *sc, void *arg, u_long ioctlCmd) { struct mrsas_iocpacket *user_ioc = (struct mrsas_iocpacket *)arg; #ifdef COMPAT_FREEBSD32 struct mrsas_iocpacket32 *user_ioc32 = (struct mrsas_iocpacket32 *)arg; #endif union mrsas_frame *in_cmd = (union mrsas_frame *)&(user_ioc->frame.raw); struct mrsas_mfi_cmd *cmd = NULL; bus_dma_tag_t ioctl_data_tag[MAX_IOCTL_SGE]; bus_dmamap_t ioctl_data_dmamap[MAX_IOCTL_SGE]; void *ioctl_data_mem[MAX_IOCTL_SGE]; bus_addr_t ioctl_data_phys_addr[MAX_IOCTL_SGE]; bus_dma_tag_t ioctl_sense_tag = 0; bus_dmamap_t ioctl_sense_dmamap = 0; void *ioctl_sense_mem = 0; bus_addr_t ioctl_sense_phys_addr = 0; int i, ioctl_data_size = 0, ioctl_sense_size, ret = 0; struct mrsas_sge32 *kern_sge32; unsigned long *sense_ptr; uint8_t *iov_base_ptrin = NULL; size_t iov_len = 0; /* * Check for NOP from MegaCli... MegaCli can issue a DCMD of 0. In * this case do nothing and return 0 to it as status. */ if (in_cmd->dcmd.opcode == 0) { device_printf(sc->mrsas_dev, "In %s() Got a NOP\n", __func__); user_ioc->frame.hdr.cmd_status = MFI_STAT_OK; return (0); } /* Validate SGL length */ if (user_ioc->sge_count > MAX_IOCTL_SGE) { device_printf(sc->mrsas_dev, "In %s() SGL is too long (%d > 8).\n", __func__, user_ioc->sge_count); return (ENOENT); } /* 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); } /* * User's IOCTL packet has 2 frames (maximum). Copy those two frames * into our cmd's frames. cmd->frame's context will get overwritten * when we copy from user's frames. So set that value alone * separately */ memcpy(cmd->frame, user_ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE); cmd->frame->hdr.context = cmd->index; cmd->frame->hdr.pad_0 = 0; cmd->frame->hdr.flags &= ~(MFI_FRAME_IEEE | MFI_FRAME_SGL64 | MFI_FRAME_SENSE64); /* * The management interface between applications and the fw uses MFI * frames. E.g, RAID configuration changes, LD property changes etc * are accomplishes through different kinds of MFI frames. The driver * needs to care only about substituting user buffers with kernel * buffers in SGLs. The location of SGL is embedded in the struct * iocpacket itself. */ kern_sge32 = (struct mrsas_sge32 *) ((unsigned long)cmd->frame + user_ioc->sgl_off); /* * For each user buffer, create a mirror buffer and copy in */ for (i = 0; i < user_ioc->sge_count; i++) { if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) { if (!user_ioc->sgl[i].iov_len) continue; ioctl_data_size = user_ioc->sgl[i].iov_len; #ifdef COMPAT_FREEBSD32 } else { if (!user_ioc32->sgl[i].iov_len) continue; ioctl_data_size = user_ioc32->sgl[i].iov_len; #endif } if (bus_dma_tag_create(sc->mrsas_parent_tag, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, ioctl_data_size, 1, ioctl_data_size, BUS_DMA_ALLOCNOW, NULL, NULL, &ioctl_data_tag[i])) { device_printf(sc->mrsas_dev, "Cannot allocate ioctl data tag\n"); ret = ENOMEM; goto out; } if (bus_dmamem_alloc(ioctl_data_tag[i], (void **)&ioctl_data_mem[i], (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_data_dmamap[i])) { device_printf(sc->mrsas_dev, "Cannot allocate ioctl data mem\n"); ret = ENOMEM; goto out; } if (bus_dmamap_load(ioctl_data_tag[i], ioctl_data_dmamap[i], ioctl_data_mem[i], ioctl_data_size, mrsas_alloc_cb, &ioctl_data_phys_addr[i], BUS_DMA_NOWAIT)) { device_printf(sc->mrsas_dev, "Cannot load ioctl data mem\n"); ret = ENOMEM; goto out; } /* Save the physical address and length */ kern_sge32[i].phys_addr = (u_int32_t)ioctl_data_phys_addr[i]; if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) { kern_sge32[i].length = user_ioc->sgl[i].iov_len; iov_base_ptrin = user_ioc->sgl[i].iov_base; iov_len = user_ioc->sgl[i].iov_len; #ifdef COMPAT_FREEBSD32 } else { kern_sge32[i].length = user_ioc32->sgl[i].iov_len; iov_base_ptrin = PTRIN(user_ioc32->sgl[i].iov_base); iov_len = user_ioc32->sgl[i].iov_len; #endif } /* Copy in data from user space */ ret = copyin(iov_base_ptrin, ioctl_data_mem[i], iov_len); if (ret) { device_printf(sc->mrsas_dev, "IOCTL copyin failed!\n"); goto out; } } ioctl_sense_size = user_ioc->sense_len; if (user_ioc->sense_len) { if (bus_dma_tag_create(sc->mrsas_parent_tag, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, ioctl_sense_size, 1, ioctl_sense_size, BUS_DMA_ALLOCNOW, NULL, NULL, &ioctl_sense_tag)) { device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense tag\n"); ret = ENOMEM; goto out; } if (bus_dmamem_alloc(ioctl_sense_tag, (void **)&ioctl_sense_mem, (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_sense_dmamap)) { device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense mem\n"); ret = ENOMEM; goto out; } if (bus_dmamap_load(ioctl_sense_tag, ioctl_sense_dmamap, ioctl_sense_mem, ioctl_sense_size, mrsas_alloc_cb, &ioctl_sense_phys_addr, BUS_DMA_NOWAIT)) { device_printf(sc->mrsas_dev, "Cannot load ioctl sense mem\n"); ret = ENOMEM; goto out; } sense_ptr = (unsigned long *)((unsigned long)cmd->frame + user_ioc->sense_off); sense_ptr = ioctl_sense_mem; } /* * 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; /* * copy out the kernel buffers to user buffers */ for (i = 0; i < user_ioc->sge_count; i++) { if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) { iov_base_ptrin = user_ioc->sgl[i].iov_base; iov_len = user_ioc->sgl[i].iov_len; #ifdef COMPAT_FREEBSD32 } else { iov_base_ptrin = PTRIN(user_ioc32->sgl[i].iov_base); iov_len = user_ioc32->sgl[i].iov_len; #endif } ret = copyout(ioctl_data_mem[i], iov_base_ptrin, iov_len); if (ret) { device_printf(sc->mrsas_dev, "IOCTL copyout failed!\n"); goto out; } } /* * copy out the sense */ if (user_ioc->sense_len) { /* * sense_buff points to the location that has the user sense * buffer address */ sense_ptr = (unsigned long *)((unsigned long)user_ioc->frame.raw + user_ioc->sense_off); ret = copyout(ioctl_sense_mem, (unsigned long *)*sense_ptr, user_ioc->sense_len); if (ret) { device_printf(sc->mrsas_dev, "IOCTL sense copyout failed!\n"); goto out; } } /* * Return command status to user space */ memcpy(&user_ioc->frame.hdr.cmd_status, &cmd->frame->hdr.cmd_status, sizeof(u_int8_t)); out: /* * Release sense buffer */ if (ioctl_sense_phys_addr) bus_dmamap_unload(ioctl_sense_tag, ioctl_sense_dmamap); if (ioctl_sense_mem != NULL) bus_dmamem_free(ioctl_sense_tag, ioctl_sense_mem, ioctl_sense_dmamap); if (ioctl_sense_tag != NULL) bus_dma_tag_destroy(ioctl_sense_tag); /* * Release data buffers */ for (i = 0; i < user_ioc->sge_count; i++) { if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) { if (!user_ioc->sgl[i].iov_len) continue; #ifdef COMPAT_FREEBSD32 } else { if (!user_ioc32->sgl[i].iov_len) continue; #endif } if (ioctl_data_phys_addr[i]) bus_dmamap_unload(ioctl_data_tag[i], ioctl_data_dmamap[i]); if (ioctl_data_mem[i] != NULL) bus_dmamem_free(ioctl_data_tag[i], ioctl_data_mem[i], ioctl_data_dmamap[i]); if (ioctl_data_tag[i] != NULL) bus_dma_tag_destroy(ioctl_data_tag[i]); } /* Free command */ mrsas_release_mfi_cmd(cmd); return (ret); }