mrsas_ioctl.c revision 272741
1/*
2 * Copyright (c) 2014, LSI Corp.
3 * All rights reserved.
4 * Author: Marian Choy
5 * Support: freebsdraid@lsi.com
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in
15 *    the documentation and/or other materials provided with the
16 *    distribution.
17 * 3. Neither the name of the <ORGANIZATION> nor the names of its
18 *    contributors may be used to endorse or promote products derived
19 *    from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 * The views and conclusions contained in the software and documentation
35 * are those of the authors and should not be interpreted as representing
36 * official policies,either expressed or implied, of the FreeBSD Project.
37 *
38 * Send feedback to: <megaraidfbsd@lsi.com>
39 * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
40 *    ATTN: MegaRaid FreeBSD
41 *
42 */
43
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD: head/sys/dev/mrsas/mrsas_ioctl.c 272741 2014-10-08 09:37:47Z kadesai $");
46
47#include <dev/mrsas/mrsas.h>
48#include <dev/mrsas/mrsas_ioctl.h>
49
50/*
51 * Function prototypes
52 */
53int mrsas_alloc_mfi_cmds(struct mrsas_softc *sc);
54int mrsas_passthru(struct mrsas_softc *sc, void *arg, u_long ioctlCmd);
55void mrsas_free_ioc_cmd(struct mrsas_softc *sc);
56void mrsas_free_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd);
57void * mrsas_alloc_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd);
58static int mrsas_create_frame_pool(struct mrsas_softc *sc);
59static void mrsas_alloc_cb(void *arg, bus_dma_segment_t *segs,
60        int nsegs, int error);
61
62extern struct mrsas_mfi_cmd* mrsas_get_mfi_cmd(struct mrsas_softc *sc);
63extern void mrsas_release_mfi_cmd(struct mrsas_mfi_cmd *cmd);
64extern int mrsas_issue_blocked_cmd(struct mrsas_softc *sc,
65    struct mrsas_mfi_cmd *cmd);
66
67/**
68 * mrsas_passthru:        Handle pass-through commands
69 * input:                 Adapter instance soft state
70 *                        argument pointer
71 *
72 * This function is called from mrsas_ioctl() to handle pass-through and
73 * ioctl commands to Firmware.
74 */
75int mrsas_passthru( struct mrsas_softc *sc, void *arg, u_long ioctlCmd )
76{
77    struct mrsas_iocpacket *user_ioc = (struct mrsas_iocpacket *)arg;
78#ifdef COMPAT_FREEBSD32
79    struct mrsas_iocpacket32 *user_ioc32 = (struct mrsas_iocpacket32 *)arg;
80#endif
81    union  mrsas_frame *in_cmd = (union mrsas_frame *) &(user_ioc->frame.raw);
82    struct mrsas_mfi_cmd *cmd = NULL;
83    bus_dma_tag_t ioctl_data_tag[MAX_IOCTL_SGE];
84    bus_dmamap_t ioctl_data_dmamap[MAX_IOCTL_SGE];
85    void *ioctl_data_mem[MAX_IOCTL_SGE];  // ioctl data virtual addr
86    bus_addr_t ioctl_data_phys_addr[MAX_IOCTL_SGE]; // ioctl data phys addr
87    bus_dma_tag_t ioctl_sense_tag = 0;
88    bus_dmamap_t ioctl_sense_dmamap = 0;
89    void *ioctl_sense_mem = 0;
90    bus_addr_t ioctl_sense_phys_addr = 0;
91    int i, ioctl_data_size=0, ioctl_sense_size, ret=0;
92    struct mrsas_sge32 *kern_sge32;
93    unsigned long *sense_ptr;
94    uint8_t *iov_base_ptrin=NULL;
95    size_t iov_len=0;
96
97    /*
98     * Check for NOP from MegaCli... MegaCli can issue a DCMD of 0.  In this
99     * case do nothing and return 0 to it as status.
100     */
101    if (in_cmd->dcmd.opcode == 0) {
102        device_printf(sc->mrsas_dev, "In %s() Got a NOP\n", __func__);
103        user_ioc->frame.hdr.cmd_status = MFI_STAT_OK;
104        return (0);
105    }
106
107    /* Validate SGL length */
108    if (user_ioc->sge_count > MAX_IOCTL_SGE) {
109        device_printf(sc->mrsas_dev, "In %s() SGL is too long (%d > 8).\n",
110            __func__, user_ioc->sge_count);
111        return(ENOENT);
112    }
113
114    /* Get a command */
115    cmd = mrsas_get_mfi_cmd(sc);
116    if (!cmd) {
117        device_printf(sc->mrsas_dev, "Failed to get a free cmd for IOCTL\n");
118        return(ENOMEM);
119    }
120
121    /*
122     * User's IOCTL packet has 2 frames (maximum). Copy those two
123     * frames into our cmd's frames. cmd->frame's context will get
124     * overwritten when we copy from user's frames. So set that value
125     * alone separately
126     */
127    memcpy(cmd->frame, user_ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE);
128    cmd->frame->hdr.context = cmd->index;
129    cmd->frame->hdr.pad_0 = 0;
130    cmd->frame->hdr.flags &= ~(MFI_FRAME_IEEE | MFI_FRAME_SGL64 |
131                               MFI_FRAME_SENSE64);
132
133    /*
134     * The management interface between applications and the fw uses
135     * MFI frames. E.g, RAID configuration changes, LD property changes
136     * etc are accomplishes through different kinds of MFI frames. The
137     * driver needs to care only about substituting user buffers with
138     * kernel buffers in SGLs. The location of SGL is embedded in the
139     * struct iocpacket itself.
140     */
141    kern_sge32 = (struct mrsas_sge32 *)
142        ((unsigned long)cmd->frame + user_ioc->sgl_off);
143
144    /*
145     * For each user buffer, create a mirror buffer and copy in
146     */
147    for (i=0; i < user_ioc->sge_count; i++) {
148	    if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
149		    if (!user_ioc->sgl[i].iov_len)
150			    continue;
151		    ioctl_data_size = user_ioc->sgl[i].iov_len;
152#ifdef COMPAT_FREEBSD32
153	    } else {
154		    if (!user_ioc32->sgl[i].iov_len)
155			    continue;
156		    ioctl_data_size = user_ioc32->sgl[i].iov_len;
157#endif
158	    }
159        if (bus_dma_tag_create( sc->mrsas_parent_tag,   // parent
160                                1, 0,                   // algnmnt, boundary
161                                BUS_SPACE_MAXADDR_32BIT,// lowaddr
162                                BUS_SPACE_MAXADDR,      // highaddr
163                                NULL, NULL,             // filter, filterarg
164                                ioctl_data_size,        // maxsize
165                                1,                      // msegments
166                                ioctl_data_size,        // maxsegsize
167                                BUS_DMA_ALLOCNOW,       // flags
168                                NULL, NULL,             // lockfunc, lockarg
169                                &ioctl_data_tag[i])) {
170		device_printf(sc->mrsas_dev, "Cannot allocate ioctl data tag\n");
171		ret = ENOMEM;
172		goto out;
173        }
174        if (bus_dmamem_alloc(ioctl_data_tag[i], (void **)&ioctl_data_mem[i],
175                (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_data_dmamap[i])) {
176            device_printf(sc->mrsas_dev, "Cannot allocate ioctl data mem\n");
177			ret = ENOMEM;
178			goto out;
179        }
180        if (bus_dmamap_load(ioctl_data_tag[i], ioctl_data_dmamap[i],
181                ioctl_data_mem[i], ioctl_data_size, mrsas_alloc_cb,
182                &ioctl_data_phys_addr[i], BUS_DMA_NOWAIT)) {
183            device_printf(sc->mrsas_dev, "Cannot load ioctl data mem\n");
184			ret = ENOMEM;
185			goto out;
186        }
187
188        /* Save the physical address and length */
189        kern_sge32[i].phys_addr = (u_int32_t)ioctl_data_phys_addr[i];
190
191	if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
192		kern_sge32[i].length = user_ioc->sgl[i].iov_len;
193
194		iov_base_ptrin = user_ioc->sgl[i].iov_base;
195		iov_len = user_ioc->sgl[i].iov_len;
196#ifdef COMPAT_FREEBSD32
197	} else {
198		kern_sge32[i].length = user_ioc32->sgl[i].iov_len;
199
200		iov_base_ptrin = PTRIN(user_ioc32->sgl[i].iov_base);
201		iov_len = user_ioc32->sgl[i].iov_len;
202#endif
203	}
204
205        /* Copy in data from user space */
206	ret = copyin(iov_base_ptrin, ioctl_data_mem[i], iov_len);
207        if (ret) {
208		device_printf(sc->mrsas_dev, "IOCTL copyin failed!\n");
209		goto out;
210        }
211    }
212
213    ioctl_sense_size = user_ioc->sense_len;
214
215    if (user_ioc->sense_len) {
216        if (bus_dma_tag_create( sc->mrsas_parent_tag,   // parent
217                                1, 0,                   // algnmnt, boundary
218                                BUS_SPACE_MAXADDR_32BIT,// lowaddr
219                                BUS_SPACE_MAXADDR,      // highaddr
220                                NULL, NULL,             // filter, filterarg
221                                ioctl_sense_size,       // maxsize
222                                1,                      // msegments
223                                ioctl_sense_size,       // maxsegsize
224                                BUS_DMA_ALLOCNOW,       // flags
225                                NULL, NULL,             // lockfunc, lockarg
226                                &ioctl_sense_tag)) {
227            device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense tag\n");
228            ret = ENOMEM;
229			goto out;
230        }
231        if (bus_dmamem_alloc(ioctl_sense_tag, (void **)&ioctl_sense_mem,
232                (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_sense_dmamap)) {
233            device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense mem\n");
234            ret = ENOMEM;
235			goto out;
236        }
237        if (bus_dmamap_load(ioctl_sense_tag, ioctl_sense_dmamap,
238                ioctl_sense_mem, ioctl_sense_size, mrsas_alloc_cb,
239                &ioctl_sense_phys_addr, BUS_DMA_NOWAIT)) {
240            device_printf(sc->mrsas_dev, "Cannot load ioctl sense mem\n");
241            ret = ENOMEM;
242			goto out;
243        }
244        sense_ptr =
245            (unsigned long *)((unsigned long)cmd->frame + user_ioc->sense_off);
246            sense_ptr = ioctl_sense_mem;
247    }
248
249    /*
250     * Set the sync_cmd flag so that the ISR knows not to complete this
251     * cmd to the SCSI mid-layer
252     */
253    cmd->sync_cmd = 1;
254    mrsas_issue_blocked_cmd(sc, cmd);
255    cmd->sync_cmd = 0;
256
257    /*
258     * copy out the kernel buffers to user buffers
259     */
260    for (i = 0; i < user_ioc->sge_count; i++) {
261	    if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
262		    iov_base_ptrin = user_ioc->sgl[i].iov_base;
263		    iov_len = user_ioc->sgl[i].iov_len;
264#ifdef COMPAT_FREEBSD32
265	    } else {
266		    iov_base_ptrin = PTRIN(user_ioc32->sgl[i].iov_base);
267		    iov_len = user_ioc32->sgl[i].iov_len;
268#endif
269	    }
270
271        ret = copyout(ioctl_data_mem[i], iov_base_ptrin, iov_len);
272        if (ret) {
273            device_printf(sc->mrsas_dev, "IOCTL copyout failed!\n");
274            goto out;
275        }
276    }
277
278    /*
279     * copy out the sense
280     */
281    if (user_ioc->sense_len) {
282        /*
283         * sense_buff points to the location that has the user
284         * sense buffer address
285         */
286        sense_ptr = (unsigned long *) ((unsigned long)user_ioc->frame.raw +
287                      user_ioc->sense_off);
288        ret = copyout(ioctl_sense_mem, (unsigned long*)*sense_ptr,
289                      user_ioc->sense_len);
290        if (ret) {
291            device_printf(sc->mrsas_dev, "IOCTL sense copyout failed!\n");
292            goto out;
293        }
294    }
295
296    /*
297     * Return command status to user space
298     */
299    memcpy(&user_ioc->frame.hdr.cmd_status, &cmd->frame->hdr.cmd_status,
300            sizeof(u_int8_t));
301
302out:
303    /*
304     * Release sense buffer
305     */
306    if (ioctl_sense_phys_addr)
307        bus_dmamap_unload(ioctl_sense_tag, ioctl_sense_dmamap);
308    if (ioctl_sense_mem != NULL)
309        bus_dmamem_free(ioctl_sense_tag, ioctl_sense_mem, ioctl_sense_dmamap);
310    if (ioctl_sense_tag != NULL)
311        bus_dma_tag_destroy(ioctl_sense_tag);
312
313    /*
314     * Release data buffers
315     */
316    for (i = 0; i < user_ioc->sge_count; i++) {
317	    if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
318		    if (!user_ioc->sgl[i].iov_len)
319			    continue;
320#ifdef COMPAT_FREEBSD32
321	    } else {
322		    if (!user_ioc32->sgl[i].iov_len)
323			    continue;
324#endif
325	    }
326        if (ioctl_data_phys_addr[i])
327            bus_dmamap_unload(ioctl_data_tag[i], ioctl_data_dmamap[i]);
328        if (ioctl_data_mem[i] != NULL)
329            bus_dmamem_free(ioctl_data_tag[i], ioctl_data_mem[i],
330                ioctl_data_dmamap[i]);
331        if (ioctl_data_tag[i] != NULL)
332            bus_dma_tag_destroy(ioctl_data_tag[i]);
333    }
334    /* Free command */
335    mrsas_release_mfi_cmd(cmd);
336
337    return(ret);
338}
339
340/**
341 * mrsas_alloc_mfi_cmds:  Allocates the command packets
342 * input:                 Adapter instance soft state
343 *
344 * Each IOCTL or passthru command that is issued to the FW are wrapped in a
345 * local data structure called mrsas_mfi_cmd.  The frame embedded in this
346 * mrsas_mfi is issued to FW. The array is used only to look up the
347 * mrsas_mfi_cmd given the context. The free commands are maintained in a
348 * linked list.
349 */
350int mrsas_alloc_mfi_cmds(struct mrsas_softc *sc)
351{
352    int i, j;
353    u_int32_t max_cmd;
354    struct mrsas_mfi_cmd *cmd;
355
356    max_cmd = MRSAS_MAX_MFI_CMDS;
357
358    /*
359     * sc->mfi_cmd_list is an array of struct mrsas_mfi_cmd pointers. Allocate the
360     * dynamic array first and then allocate individual commands.
361     */
362    sc->mfi_cmd_list = malloc(sizeof(struct mrsas_mfi_cmd*)*max_cmd, M_MRSAS, M_NOWAIT);
363    if (!sc->mfi_cmd_list) {
364        device_printf(sc->mrsas_dev, "Cannot alloc memory for mfi_cmd cmd_list.\n");
365        return(ENOMEM);
366    }
367    memset(sc->mfi_cmd_list, 0, sizeof(struct mrsas_mfi_cmd *)*max_cmd);
368    for (i = 0; i < max_cmd; i++) {
369        sc->mfi_cmd_list[i] = malloc(sizeof(struct mrsas_mfi_cmd),
370                                 M_MRSAS, M_NOWAIT);
371        if (!sc->mfi_cmd_list[i]) {
372            for (j = 0; j < i; j++)
373                free(sc->mfi_cmd_list[j],M_MRSAS);
374            free(sc->mfi_cmd_list, M_MRSAS);
375            sc->mfi_cmd_list = NULL;
376            return(ENOMEM);
377        }
378    }
379
380    for (i = 0; i < max_cmd; i++) {
381        cmd = sc->mfi_cmd_list[i];
382        memset(cmd, 0, sizeof(struct mrsas_mfi_cmd));
383        cmd->index = i;
384        cmd->ccb_ptr = NULL;
385        cmd->sc = sc;
386        TAILQ_INSERT_TAIL(&(sc->mrsas_mfi_cmd_list_head), cmd, next);
387    }
388
389    /* create a frame pool and assign one frame to each command */
390    if (mrsas_create_frame_pool(sc)) {
391        device_printf(sc->mrsas_dev, "Cannot allocate DMA frame pool.\n");
392        for (i = 0; i < MRSAS_MAX_MFI_CMDS; i++) { // Free the frames
393            cmd = sc->mfi_cmd_list[i];
394            mrsas_free_frame(sc, cmd);
395        }
396        if (sc->mficmd_frame_tag != NULL)
397            bus_dma_tag_destroy(sc->mficmd_frame_tag);
398        return(ENOMEM);
399    }
400
401    return(0);
402}
403
404/**
405 * mrsas_create_frame_pool -   Creates DMA pool for cmd frames
406 * input:                      Adapter soft state
407 *
408 * Each command packet has an embedded DMA memory buffer that is used for
409 * filling MFI frame and the SG list that immediately follows the frame. This
410 * function creates those DMA memory buffers for each command packet by using
411 * PCI pool facility. pad_0 is initialized to 0 to prevent corrupting value
412 * of context and could cause FW crash.
413 */
414static int mrsas_create_frame_pool(struct mrsas_softc *sc)
415{
416    int i;
417    struct mrsas_mfi_cmd *cmd;
418
419    if (bus_dma_tag_create( sc->mrsas_parent_tag,   // parent
420                            1, 0,                   // algnmnt, boundary
421                            BUS_SPACE_MAXADDR_32BIT,// lowaddr
422                            BUS_SPACE_MAXADDR,      // highaddr
423                            NULL, NULL,             // filter, filterarg
424                            MRSAS_MFI_FRAME_SIZE,   // maxsize
425                            1,                      // msegments
426                            MRSAS_MFI_FRAME_SIZE,   // maxsegsize
427                            BUS_DMA_ALLOCNOW,       // flags
428                            NULL, NULL,             // lockfunc, lockarg
429                            &sc->mficmd_frame_tag)) {
430        device_printf(sc->mrsas_dev, "Cannot create MFI frame tag\n");
431        return (ENOMEM);
432    }
433
434    for (i = 0; i < MRSAS_MAX_MFI_CMDS; i++) {
435        cmd = sc->mfi_cmd_list[i];
436        cmd->frame = mrsas_alloc_frame(sc, cmd);
437        if (cmd->frame == NULL) {
438            device_printf(sc->mrsas_dev, "Cannot alloc MFI frame memory\n");
439            return (ENOMEM);
440        }
441        memset(cmd->frame, 0, MRSAS_MFI_FRAME_SIZE);
442        cmd->frame->io.context = cmd->index;
443        cmd->frame->io.pad_0 = 0;
444    }
445
446    return(0);
447}
448
449/**
450 * mrsas_alloc_frame -   Allocates MFI Frames
451 * input:                Adapter soft state
452 *
453 * Create bus DMA memory tag and dmamap and load memory for MFI frames.
454 * Returns virtual memory pointer to allocated region.
455 */
456void *mrsas_alloc_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd)
457{
458    u_int32_t frame_size = MRSAS_MFI_FRAME_SIZE;
459
460    if (bus_dmamem_alloc(sc->mficmd_frame_tag, (void **)&cmd->frame_mem,
461                    BUS_DMA_NOWAIT, &cmd->frame_dmamap)) {
462        device_printf(sc->mrsas_dev, "Cannot alloc MFI frame memory\n");
463        return (NULL);
464    }
465    if (bus_dmamap_load(sc->mficmd_frame_tag, cmd->frame_dmamap,
466                        cmd->frame_mem, frame_size, mrsas_alloc_cb,
467                        &cmd->frame_phys_addr, BUS_DMA_NOWAIT)) {
468        device_printf(sc->mrsas_dev, "Cannot load IO request memory\n");
469        return (NULL);
470    }
471
472    return(cmd->frame_mem);
473}
474
475/*
476 * mrsas_alloc_cb:  Callback function of bus_dmamap_load()
477 * input:           callback argument,
478 *                  machine dependent type that describes DMA segments,
479 *                  number of segments,
480 *                  error code.
481 *
482 * This function is for the driver to receive mapping information resultant
483 * of the bus_dmamap_load(). The information is actually not being used,
484 * but the address is saved anyway.
485 */
486static void mrsas_alloc_cb(void *arg, bus_dma_segment_t *segs,
487        int nsegs, int error)
488{
489    bus_addr_t *addr;
490
491    addr = arg;
492    *addr = segs[0].ds_addr;
493}
494
495/**
496 * mrsas_free_frames:    Frees memory for  MFI frames
497 * input:                Adapter soft state
498 *
499 * Deallocates MFI frames memory.  Called from mrsas_free_mem() during
500 * detach and error case during creation of frame pool.
501 */
502void mrsas_free_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd)
503{
504    if (cmd->frame_phys_addr)
505        bus_dmamap_unload(sc->mficmd_frame_tag, cmd->frame_dmamap);
506    if (cmd->frame_mem != NULL)
507        bus_dmamem_free(sc->mficmd_frame_tag, cmd->frame_mem, cmd->frame_dmamap);
508}
509