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