/**
 * 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);
}
Пример #2
0
/*
 * 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);
}