diff --git a/sys/dev/mfi/mfi.c b/sys/dev/mfi/mfi.c index 2fedec5c952b..e0a79f6a4ad8 100644 --- a/sys/dev/mfi/mfi.c +++ b/sys/dev/mfi/mfi.c @@ -1797,6 +1797,14 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td) { struct mfi_softc *sc; union mfi_statrequest *ms; + struct mfi_ioc_packet *ioc; + struct mfi_ioc_aen *aen; + struct mfi_command *cm = NULL; + struct mfi_dcmd_frame *dcmd; + uint32_t context; + uint32_t *sense_ptr; + uint8_t *data = NULL, *temp; + int i; int error; sc = dev->si_drv1; @@ -1818,7 +1826,133 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td) break; } break; - case 0xc1144d01: /* Firmware Linux ioctl shim */ + case MFI_CMD: + ioc = (struct mfi_ioc_packet *)arg; + + mtx_lock(&sc->mfi_io_lock); + if ((cm = mfi_dequeue_free(sc)) == NULL) { + mtx_unlock(&sc->mfi_io_lock); + return (EBUSY); + } + mtx_unlock(&sc->mfi_io_lock); + + /* + * save off original context since copying from user + * will clobber some data + */ + context = cm->cm_frame->header.context; + + bcopy(ioc->mi_frame.raw, cm->cm_frame, + ioc->mi_sgl_off); /* Linux can do 2 frames ? */ + cm->cm_total_frame_size = ioc->mi_sgl_off; + cm->cm_sg = + (union mfi_sgl *)&cm->cm_frame->bytes[ioc->mi_sgl_off]; + cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_DATAOUT + | MFI_CMD_POLLED; + cm->cm_len = cm->cm_frame->header.data_len; + cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF, + M_WAITOK | M_ZERO); + + /* restore header context */ + cm->cm_frame->header.context = context; + /* ioctl's are dcmd types */ + dcmd = &cm->cm_frame->dcmd; + + temp = data; + for (i = 0; i < ioc->mi_sge_count; i++) { + error = copyin(ioc->mi_sgl[i].iov_base, + temp, + ioc->mi_sgl[i].iov_len); + if (error != 0) { + device_printf(sc->mfi_dev, + "Copy in failed"); + goto out; + } + temp = &temp[ioc->mi_sgl[i].iov_len]; + } + + if (ioc->mi_sense_len) { + sense_ptr = + (void *)&cm->cm_frame->bytes[ioc->mi_sense_off]; + *sense_ptr = cm->cm_sense_busaddr; + } + + mtx_lock(&sc->mfi_io_lock); + if ((error = mfi_mapcmd(sc, cm)) != 0) { + device_printf(sc->mfi_dev, + "Controller info buffer map failed"); + mtx_unlock(&sc->mfi_io_lock); + goto out; + } + + if ((error = mfi_polled_command(sc, cm)) != 0) { + device_printf(sc->mfi_dev, + "Controller polled failed"); + mtx_unlock(&sc->mfi_io_lock); + goto out; + } + + bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); + mtx_unlock(&sc->mfi_io_lock); + + temp = data; + for (i = 0; i < ioc->mi_sge_count; i++) { + error = copyout(temp, + ioc->mi_sgl[i].iov_base, + ioc->mi_sgl[i].iov_len); + if (error != 0) { + device_printf(sc->mfi_dev, + "Copy out failed"); + goto out; + } + temp = &temp[ioc->mi_sgl[i].iov_len]; + } + + if (ioc->mi_sense_len) { + /* copy out sense */ + sense_ptr = (void *) + &ioc->mi_frame.raw[ioc->mi_sense_off]; + temp = 0; + temp += cm->cm_sense_busaddr; + error = copyout(temp, sense_ptr, + ioc->mi_sense_len); + if (error != 0) { + device_printf(sc->mfi_dev, + "Copy out failed"); + goto out; + } + } + + ioc->mi_frame.hdr.cmd_status = cm->cm_frame->header.cmd_status; + if (cm->cm_frame->header.cmd_status == MFI_STAT_OK) { + switch (dcmd->opcode) { + case MFI_DCMD_CFG_CLEAR: + case MFI_DCMD_CFG_ADD: +/* + mfi_ldrescan(sc); +*/ + break; + } + } +out: + if (data) + free(data, M_MFIBUF); + if (cm) { + mtx_lock(&sc->mfi_io_lock); + mfi_release_command(cm); + mtx_unlock(&sc->mfi_io_lock); + } + + break; + case MFI_SET_AEN: + aen = (struct mfi_ioc_aen *)arg; + error = mfi_aen_register(sc, aen->aen_seq_num, + aen->aen_class_locale); + + break; + case MFI_LINUX_CMD_2: /* Firmware Linux ioctl shim */ { devclass_t devclass; struct mfi_linux_ioc_packet l_ioc; @@ -1839,7 +1973,7 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td) cmd, arg, flag, td)); break; } - case 0x400c4d03: /* AEN Linux ioctl shim */ + case MFI_LINUX_SET_AEN_2: /* AEN Linux ioctl shim */ { devclass_t devclass; struct mfi_linux_ioc_aen l_aen; @@ -1880,13 +2014,14 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_threa uint32_t *sense_ptr; uint32_t context; uint8_t *data = NULL, *temp; + void *temp_convert; int i; int error; sc = dev->si_drv1; error = 0; switch (cmd) { - case 0xc1144d01: /* Firmware Linux ioctl shim */ + case MFI_LINUX_CMD_2: /* Firmware Linux ioctl shim */ error = copyin(arg, &l_ioc, sizeof(l_ioc)); if (error != 0) return (error); @@ -1924,7 +2059,9 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_threa temp = data; for (i = 0; i < l_ioc.lioc_sge_count; i++) { - error = copyin(l_ioc.lioc_sgl[i].iov_base, + temp_convert = + (void *)(uintptr_t)l_ioc.lioc_sgl[i].iov_base; + error = copyin(temp_convert, temp, l_ioc.lioc_sgl[i].iov_len); if (error != 0) { @@ -1963,8 +2100,10 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_threa temp = data; for (i = 0; i < l_ioc.lioc_sge_count; i++) { + temp_convert = + (void *)(uintptr_t)l_ioc.lioc_sgl[i].iov_base; error = copyout(temp, - l_ioc.lioc_sgl[i].iov_base, + temp_convert, l_ioc.lioc_sgl[i].iov_len); if (error != 0) { device_printf(sc->mfi_dev, @@ -2017,7 +2156,7 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_threa } return (error); - case 0x400c4d03: /* AEN Linux ioctl shim */ + case MFI_LINUX_SET_AEN_2: /* AEN Linux ioctl shim */ error = copyin(arg, &l_aen, sizeof(l_aen)); if (error != 0) return (error); diff --git a/sys/dev/mfi/mfi_ioctl.h b/sys/dev/mfi/mfi_ioctl.h index 7c4dae929065..8b43a794642b 100644 --- a/sys/dev/mfi/mfi_ioctl.h +++ b/sys/dev/mfi/mfi_ioctl.h @@ -27,6 +27,13 @@ #include __FBSDID("$FreeBSD$"); +#if defined(__amd64__) /* Assume amd64 wants 32 bit Linux */ +struct iovec32 { + u_int32_t iov_base; + int iov_len; +}; +#endif + #define MFIQ_FREE 0 #define MFIQ_BIO 1 #define MFIQ_READY 2 @@ -43,6 +50,33 @@ union mfi_statrequest { struct mfi_qstat ms_qstat; }; +#define MAX_IOCTL_SGE 16 + +struct mfi_ioc_packet { + uint16_t mi_adapter_no; + uint16_t mi_pad1; + uint32_t mi_sgl_off; + uint32_t mi_sge_count; + uint32_t mi_sense_off; + uint32_t mi_sense_len; + union { + uint8_t raw[128]; + struct mfi_frame_header hdr; + } mi_frame; + + struct iovec mi_sgl[MAX_IOCTL_SGE]; +} __packed; + +struct mfi_ioc_aen { + uint16_t aen_adapter_no; + uint16_t aen_pad1; + uint32_t aen_seq_num; + uint32_t aen_class_locale; +} __packed; + +#define MFI_CMD _IOWR('M', 1, struct mfi_ioc_packet) +#define MFI_SET_AEN _IOW('M', 3, struct mfi_ioc_aen) + #define MAX_LINUX_IOCTL_SGE 16 struct mfi_linux_ioc_packet { @@ -57,7 +91,11 @@ struct mfi_linux_ioc_packet { struct mfi_frame_header hdr; } lioc_frame; +#if defined(__amd64__) /* Assume amd64 wants 32 bit Linux */ + struct iovec32 lioc_sgl[MAX_LINUX_IOCTL_SGE]; +#else struct iovec lioc_sgl[MAX_LINUX_IOCTL_SGE]; +#endif } __packed; #define MFIIO_STATS _IOWR('Q', 101, union mfi_statrequest) @@ -68,3 +106,12 @@ struct mfi_linux_ioc_aen { uint32_t laen_seq_num; uint32_t laen_class_locale; } __packed; + +/* + * Create a second set so the FreeBSD native ioctl doesn't + * conflict in FreeBSD ioctl handler. Translate in mfi_linux.c. + */ +#define MFI_LINUX_CMD 0xc1144d01 +#define MFI_LINUX_SET_AEN 0x400c4d03 +#define MFI_LINUX_CMD_2 0xc1144d02 +#define MFI_LINUX_SET_AEN_2 0x400c4d04 diff --git a/sys/dev/mfi/mfi_linux.c b/sys/dev/mfi/mfi_linux.c index e12d6067b9e7..911c02c9059c 100644 --- a/sys/dev/mfi/mfi_linux.c +++ b/sys/dev/mfi/mfi_linux.c @@ -45,6 +45,9 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include + /* There are multiple ioctl number ranges that need to be handled */ #define MFI_LINUX_IOCTL_MIN 0x4d00 #define MFI_LINUX_IOCTL_MAX 0x4d04 @@ -81,10 +84,20 @@ mfi_linux_ioctl(d_thread_t *p, struct linux_ioctl_args *args) { struct file *fp; int error; + u_long cmd = args->cmd; + + switch (cmd) { + case MFI_LINUX_CMD: + cmd = MFI_LINUX_CMD_2; + break; + case MFI_LINUX_SET_AEN: + cmd = MFI_LINUX_SET_AEN_2; + break; + } if ((error = fget(p, args->fd, &fp)) != 0) return (error); - error = fo_ioctl(fp, args->cmd, (caddr_t)args->arg, p->td_ucred, p); + error = fo_ioctl(fp, cmd, (caddr_t)args->arg, p->td_ucred, p); fdrop(fp, p); return (error); }