tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: pass-through linux ioctl for mfi(4)



Hello,
so it seems we can't do much better in compat_linux.
Here's an updated patch, which checks the size before malloc in mfifioctl(),
and I also removed a debug printf in compat_linux.
I intend to commit this next weekend.

-- 
Manuel Bouyer <bouyer%antioche.eu.org@localhost>
     NetBSD: 26 ans d'experience feront toujours la difference
--
Index: dev/ic/mfi.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mfi.c,v
retrieving revision 1.46
diff -u -p -u -r1.46 mfi.c
--- dev/ic/mfi.c        26 Aug 2012 16:22:32 -0000      1.46
+++ dev/ic/mfi.c        18 Sep 2012 22:35:38 -0000
@@ -86,6 +86,8 @@ __KERNEL_RCSID(0, "$NetBSD: mfi.c,v 1.46
 #include <sys/malloc.h>
 #include <sys/proc.h>
 #include <sys/cpu.h>
+#include <sys/conf.h>
+#include <sys/kauth.h>
 
 #include <uvm/uvm_param.h>
 
@@ -100,6 +102,7 @@ __KERNEL_RCSID(0, "$NetBSD: mfi.c,v 1.46
 
 #include <dev/ic/mfireg.h>
 #include <dev/ic/mfivar.h>
+#include <dev/ic/mfiio.h>
 
 #if NBIO > 0
 #include <dev/biovar.h>
@@ -177,6 +180,16 @@ static bool                mfi_shutdown(device_t, int)
 static bool            mfi_suspend(device_t, const pmf_qual_t *);
 static bool            mfi_resume(device_t, const pmf_qual_t *);
 
+static dev_type_open(mfifopen);
+static dev_type_close(mfifclose);
+static dev_type_ioctl(mfifioctl);
+const struct cdevsw mfi_cdevsw = {
+       mfifopen, mfifclose, noread, nowrite, mfifioctl,
+       nostop, notty, nopoll, nommap, nokqfilter, D_OTHER
+};
+
+extern struct cfdriver mfi_cd;
+
 static uint32_t        mfi_xscale_fw_state(struct mfi_softc *sc);
 static void            mfi_xscale_intr_ena(struct mfi_softc *sc);
 static void            mfi_xscale_intr_dis(struct mfi_softc *sc);
@@ -3472,3 +3485,151 @@ mfi_sync_map_complete(struct mfi_ccb *cc
                workqueue_enqueue(sc->sc_ldsync_wq, &sc->sc_ldsync_wk, NULL);
        }
 }
+
+static int
+mfifopen(dev_t dev, int flag, int mode, struct lwp *l)
+{
+       struct mfi_softc *sc;
+
+       if ((sc = device_lookup_private(&mfi_cd, minor(dev))) == NULL)
+               return (ENXIO);
+       return (0);
+}
+
+static int
+mfifclose(dev_t dev, int flag, int mode, struct lwp *l)
+{
+       struct mfi_softc *sc;
+
+       sc = device_lookup_private(&mfi_cd, minor(dev));
+       return (0);
+}
+
+static int
+mfifioctl(dev_t dev, u_long cmd, void *data, int flag,
+    struct lwp *l)
+{
+       struct mfi_softc *sc;
+       struct mfi_ioc_packet *ioc = data;
+       uint8_t *udata;
+       struct mfi_ccb *ccb = NULL;
+       int ctx, i, s, error;
+       union mfi_sense_ptr sense_ptr;
+
+       switch(cmd) {
+       case MFI_CMD:
+               sc = device_lookup_private(&mfi_cd, ioc->mfi_adapter_no);
+               break;
+       default:
+               return ENOTTY;
+       }
+       if (sc == NULL)
+               return (ENXIO);
+       if (sc->sc_opened)
+               return (EBUSY);
+
+       switch(cmd) {
+       case MFI_CMD:
+               error = kauth_authorize_device_passthru(l->l_cred, dev,
+                   KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_ALL, data);
+               if (error)
+                       return error;
+               if (ioc->mfi_sge_count > MAX_IOCTL_SGE)
+                       return EINVAL;
+               s = splbio();
+               if ((ccb = mfi_get_ccb(sc)) == NULL)
+                       return ENOMEM;
+               ccb->ccb_data = NULL;
+               ctx = ccb->ccb_frame->mfr_header.mfh_context;
+               memcpy(ccb->ccb_frame, ioc->mfi_frame.raw,
+                  sizeof(*ccb->ccb_frame));
+               ccb->ccb_frame->mfr_header.mfh_context = ctx;
+               ccb->ccb_frame->mfr_header.mfh_scsi_status = 0;
+               ccb->ccb_frame->mfr_header.mfh_pad0 = 0;
+               ccb->ccb_frame_size =
+                   (sizeof(union mfi_sgl) * ioc->mfi_sge_count) +
+                   ioc->mfi_sgl_off;
+               if (ioc->mfi_sge_count > 0) {
+                       ccb->ccb_sgl = (union mfi_sgl *)
+                           &ccb->ccb_frame->mfr_bytes[ioc->mfi_sgl_off];
+               }
+               if (ccb->ccb_frame->mfr_header.mfh_flags & MFI_FRAME_DIR_READ)
+                       ccb->ccb_direction = MFI_DATA_IN;
+               if (ccb->ccb_frame->mfr_header.mfh_flags & MFI_FRAME_DIR_WRITE)
+                       ccb->ccb_direction = MFI_DATA_OUT;
+               ccb->ccb_len = ccb->ccb_frame->mfr_header.mfh_data_len;
+               if (ccb->ccb_len > MAXPHYS) {
+                       error = ENOMEM;
+                       goto out;
+               }
+               if (ccb->ccb_len &&
+                   (ccb->ccb_direction & (MFI_DATA_IN | MFI_DATA_OUT)) != 0) {
+                       udata = malloc(ccb->ccb_len, M_DEVBUF, M_WAITOK|M_ZERO);
+                       if (udata == NULL) {
+                               error = ENOMEM;
+                               goto out;
+                       }
+                       ccb->ccb_data = udata;
+                       if (ccb->ccb_direction & MFI_DATA_OUT) {
+                               for (i = 0; i < ioc->mfi_sge_count; i++) {
+                                       error = copyin(ioc->mfi_sgl[i].iov_base,
+                                           udata, ioc->mfi_sgl[i].iov_len);
+                                       if (error)
+                                               goto out;
+                                       udata = &udata[
+                                           ioc->mfi_sgl[i].iov_len];
+                               }
+                       }
+                       if (mfi_create_sgl(ccb, BUS_DMA_WAITOK)) {
+                               error = EIO;
+                               goto out;
+                       }
+               }
+               if (ccb->ccb_frame->mfr_header.mfh_cmd == MFI_CMD_PD_SCSI_IO) {
+                       ccb->ccb_frame->mfr_io.mif_sense_addr_lo =
+                           htole32(ccb->ccb_psense);
+                       ccb->ccb_frame->mfr_io.mif_sense_addr_hi = 0;
+               }
+               ccb->ccb_done = mfi_mgmt_done;
+               mfi_post(sc, ccb);
+               while (ccb->ccb_state != MFI_CCB_DONE)
+                       tsleep(ccb, PRIBIO, "mfi_fioc", 0);
+
+               if (ccb->ccb_direction & MFI_DATA_IN) {
+                       udata = ccb->ccb_data;
+                       for (i = 0; i < ioc->mfi_sge_count; i++) {
+                               error = copyout(udata,
+                                   ioc->mfi_sgl[i].iov_base,
+                                   ioc->mfi_sgl[i].iov_len);
+                               if (error)
+                                       goto out;
+                               udata = &udata[
+                                   ioc->mfi_sgl[i].iov_len];
+                       }
+               }
+               if (ioc->mfi_sense_len) {
+                       memcpy(&sense_ptr.sense_ptr_data[0],
+                       &ioc->mfi_frame.raw[ioc->mfi_sense_off],
+                       sizeof(sense_ptr.sense_ptr_data));
+                       error = copyout(ccb->ccb_sense,
+                           sense_ptr.user_space,
+                           sizeof(sense_ptr.sense_ptr_data));
+                       if (error)
+                               goto out;
+               }
+               memcpy(ioc->mfi_frame.raw, ccb->ccb_frame,
+                  sizeof(*ccb->ccb_frame));
+               break;
+       default:
+               printf("mfifioctl unhandled cmd 0x%lx\n", cmd);
+               return ENOTTY;
+       }
+
+out:
+       if (ccb->ccb_data)
+               free(ccb->ccb_data, M_DEVBUF);
+       if (ccb)
+               mfi_put_ccb(ccb);
+       splx(s);
+       return error;
+}
Index: dev/ic/mfiio.h
===================================================================
RCS file: dev/ic/mfiio.h
diff -N dev/ic/mfiio.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/ic/mfiio.h      18 Sep 2012 22:35:38 -0000
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 2006 IronPort Systems
+ * All rights reserved.
+ *
+ * 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 <dev/ic/mfireg.h>
+#include <sys/ioctl.h>
+
+/*
+ * these structures are used for LSI's MegaCli on both FreeBSD and Linux
+ * We will also use them in the NetBSD driver.
+ */
+
+#define MAX_SPACE_FOR_SENSE_PTR         32
+union mfi_sense_ptr {
+       uint8_t         sense_ptr_data[MAX_SPACE_FOR_SENSE_PTR];
+       void            *user_space;
+       struct {
+               uint32_t        low;
+               uint32_t        high; 
+       } addr;
+} __packed;
+
+
+#define MAX_IOCTL_SGE  16
+
+struct mfi_ioc_packet {
+       uint16_t        mfi_adapter_no;
+       uint16_t        mfi_pad1;       
+       uint32_t        mfi_sgl_off;    
+       uint32_t        mfi_sge_count;
+       uint32_t        mfi_sense_off;
+       uint32_t        mfi_sense_len;
+       union {
+               uint8_t raw[128];       
+               struct mfi_frame_header hdr;
+       } mfi_frame;
+
+       struct iovec mfi_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)
+
+#ifdef _LP64
+struct iovec32 {
+       u_int32_t       iov_base;
+       int             iov_len;
+};
+
+struct mfi_ioc_packet32 {
+       uint16_t        mfi_adapter_no;
+       uint16_t        mfi_pad1;       
+       uint32_t        mfi_sgl_off;    
+       uint32_t        mfi_sge_count;
+       uint32_t        mfi_sense_off;
+       uint32_t        mfi_sense_len;
+       union {
+               uint8_t raw[128];       
+               struct mfi_frame_header hdr;
+       } mfi_frame;
+
+       struct iovec32 mfi_sgl[MAX_IOCTL_SGE];
+} __packed;
+#define MFI_CMD32      _IOWR('M', 1, struct mfi_ioc_packet32)
+#endif
Index: dev/ic/mfireg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mfireg.h,v
retrieving revision 1.7
diff -u -p -u -r1.7 mfireg.h
--- dev/ic/mfireg.h     26 Aug 2012 16:05:29 -0000      1.7
+++ dev/ic/mfireg.h     18 Sep 2012 22:35:38 -0000
@@ -43,6 +43,9 @@
  * SUCH DAMAGE.
  */
 
+#ifndef _DEV_IC_MFIREG_H_
+#define _DEV_IC_MFIREG_H_
+
 /* management interface constants */
 #define MFI_MGMT_VD                    0x01
 #define MFI_MGMT_SD                    0x02
@@ -1703,3 +1706,5 @@ typedef union _mfi_address {
 
 #define MEGASAS_MAX_NAME        32
 #define MEGASAS_VERSION         "4.23"
+
+#endif /* _DEV_IC_MFIREG_H_ */
Index: dev/ic/mfivar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mfivar.h,v
retrieving revision 1.19
diff -u -p -u -r1.19 mfivar.h
--- dev/ic/mfivar.h     26 Aug 2012 16:05:29 -0000      1.19
+++ dev/ic/mfivar.h     18 Sep 2012 22:35:38 -0000
@@ -210,6 +210,9 @@ struct mfi_softc {
        bool                    sc_running;
 
        device_t                sc_child;
+
+       /* for ioctl interface */
+       bool                    sc_opened;
 };
 
 int    mfi_rescan(device_t, const char *, const int *);
Index: compat/linux/common/linux_ioctl.c
===================================================================
RCS file: /cvsroot/src/sys/compat/linux/common/linux_ioctl.c,v
retrieving revision 1.56
diff -u -p -u -r1.56 linux_ioctl.c
--- compat/linux/common/linux_ioctl.c   14 Oct 2011 09:23:28 -0000      1.56
+++ compat/linux/common/linux_ioctl.c   18 Sep 2012 22:35:38 -0000
@@ -63,6 +63,10 @@ __KERNEL_RCSID(0, "$NetBSD: linux_ioctl.
 #include <compat/ossaudio/ossaudio.h>
 #define LINUX_TO_OSS(v) ((const void *)(v))    /* do nothing, same ioctl() 
encoding */
 
+#include <dev/ic/mfiio.h>
+#define LINUX_MEGARAID_CMD     _LINUX_IOWR('M', 1, struct mfi_ioc_packet)
+#define LINUX_MEGARAID_GET_AEN _LINUX_IOW('M', 3, struct mfi_ioc_aen)
+
 /*
  * Most ioctl command are just converted to their NetBSD values,
  * and passed on. The ones that take structure pointers and (flag)
@@ -80,7 +84,28 @@ linux_sys_ioctl(struct lwp *l, const str
 
        switch (LINUX_IOCGROUP(SCARG(uap, com))) {
        case 'M':
-               error = oss_ioctl_mixer(l, LINUX_TO_OSS(uap), retval);
+               switch(SCARG(uap, com)) {
+               case LINUX_MEGARAID_CMD:
+               case LINUX_MEGARAID_GET_AEN:
+               {
+                       struct sys_ioctl_args ua;
+                       u_long com = 0;
+                       if (SCARG(uap, com) & IOC_IN)
+                               com |= IOC_OUT;
+                       if (SCARG(uap, com) & IOC_OUT)
+                               com |= IOC_IN;
+                       SCARG(&ua, fd) = SCARG(uap, fd);
+                       SCARG(&ua, com) = SCARG(uap, com);
+                       SCARG(&ua, com) &= ~IOC_DIRMASK;
+                       SCARG(&ua, com) |= com;
+                       SCARG(&ua, data) = SCARG(uap, data);
+                       error = sys_ioctl(l, (const void *)&ua, retval);
+                       break;
+               }
+               default:
+                       error = oss_ioctl_mixer(l, LINUX_TO_OSS(uap), retval);
+                       break;
+               }
                break;
        case 'Q':
                error = oss_ioctl_sequencer(l, LINUX_TO_OSS(uap), retval);


Home | Main Index | Thread Index | Old Index