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