xref: /illumos-gate/usr/src/uts/common/io/aac/aac_ioctl.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright 2005-06 Adaptec, Inc.
8  * Copyright (c) 2005-06 Adaptec Inc., Achim Leubner
9  * Copyright (c) 2000 Michael Smith
10  * Copyright (c) 2001 Scott Long
11  * Copyright (c) 2000 BSDi
12  * All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 #include <sys/modctl.h>
36 #include <sys/conf.h>
37 #include <sys/cmn_err.h>
38 #include <sys/ddi.h>
39 #include <sys/devops.h>
40 #include <sys/pci.h>
41 #include <sys/types.h>
42 #include <sys/ddidmareq.h>
43 #include <sys/scsi/scsi.h>
44 #include <sys/ksynch.h>
45 #include <sys/sunddi.h>
46 #include <sys/byteorder.h>
47 #include <sys/kmem.h>
48 #include "aac_regs.h"
49 #include "aac.h"
50 #include "aac_ioctl.h"
51 
52 struct aac_umem_sge {
53 	uint32_t bcount;
54 	caddr_t addr;
55 	struct aac_cmd acp;
56 };
57 
58 /*
59  * External functions
60  */
61 extern int aac_sync_mbcommand(struct aac_softstate *, uint32_t, uint32_t,
62     uint32_t, uint32_t, uint32_t, uint32_t *);
63 extern int aac_cmd_dma_alloc(struct aac_softstate *, struct aac_cmd *,
64     struct buf *, int, int (*)(), caddr_t);
65 extern void aac_free_dmamap(struct aac_cmd *);
66 extern int aac_do_io(struct aac_softstate *, struct aac_cmd *);
67 extern void aac_cmd_fib_copy(struct aac_softstate *, struct aac_cmd *);
68 extern void aac_ioctl_complete(struct aac_softstate *, struct aac_cmd *);
69 
70 extern ddi_device_acc_attr_t aac_acc_attr;
71 extern int aac_check_dma_handle(ddi_dma_handle_t);
72 
73 /*
74  * IOCTL command handling functions
75  */
76 static int aac_check_revision(struct aac_softstate *, intptr_t, int);
77 static int aac_ioctl_send_fib(struct aac_softstate *, intptr_t, int);
78 static int aac_open_getadapter_fib(struct aac_softstate *, intptr_t, int);
79 static int aac_next_getadapter_fib(struct aac_softstate *, intptr_t, int);
80 static int aac_close_getadapter_fib(struct aac_softstate *, intptr_t);
81 static int aac_send_raw_srb(struct aac_softstate *, dev_t, intptr_t, int);
82 static int aac_get_pci_info(struct aac_softstate *, intptr_t, int);
83 static int aac_query_disk(struct aac_softstate *, intptr_t, int);
84 static int aac_delete_disk(struct aac_softstate *, intptr_t, int);
85 static int aac_supported_features(struct aac_softstate *, intptr_t, int);
86 
87 /*
88  * Warlock directives
89  */
90 _NOTE(SCHEME_PROTECTS_DATA("unique to each handling function", aac_features
91     aac_pci_info aac_query_disk aac_revision aac_umem_sge))
92 
93 int
94 aac_do_ioctl(struct aac_softstate *softs, dev_t dev, int cmd, intptr_t arg,
95     int mode)
96 {
97 	int status;
98 
99 	switch (cmd) {
100 	case FSACTL_MINIPORT_REV_CHECK:
101 		AACDB_PRINT_IOCTL(softs, "FSACTL_MINIPORT_REV_CHECK");
102 		status = aac_check_revision(softs, arg, mode);
103 		break;
104 	case FSACTL_SENDFIB:
105 		AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_LARGE_FIB");
106 		goto send_fib;
107 	case FSACTL_SEND_LARGE_FIB:
108 		AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_LARGE_FIB");
109 send_fib:
110 		status = aac_ioctl_send_fib(softs, arg, mode);
111 		break;
112 	case FSACTL_OPEN_GET_ADAPTER_FIB:
113 		AACDB_PRINT_IOCTL(softs, "FSACTL_OPEN_GET_ADAPTER_FIB");
114 		status = aac_open_getadapter_fib(softs, arg, mode);
115 		break;
116 	case FSACTL_GET_NEXT_ADAPTER_FIB:
117 		AACDB_PRINT_IOCTL(softs, "FSACTL_GET_NEXT_ADAPTER_FIB");
118 		status = aac_next_getadapter_fib(softs, arg, mode);
119 		break;
120 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
121 		AACDB_PRINT_IOCTL(softs, "FSACTL_CLOSE_GET_ADAPTER_FIB");
122 		status = aac_close_getadapter_fib(softs, arg);
123 		break;
124 	case FSACTL_SEND_RAW_SRB:
125 		AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_RAW_SRB");
126 		status = aac_send_raw_srb(softs, dev, arg, mode);
127 		break;
128 	case FSACTL_GET_PCI_INFO:
129 		AACDB_PRINT_IOCTL(softs, "FSACTL_GET_PCI_INFO");
130 		status = aac_get_pci_info(softs, arg, mode);
131 		break;
132 	case FSACTL_QUERY_DISK:
133 		AACDB_PRINT_IOCTL(softs, "FSACTL_QUERY_DISK");
134 		status = aac_query_disk(softs, arg, mode);
135 		break;
136 	case FSACTL_DELETE_DISK:
137 		AACDB_PRINT_IOCTL(softs, "FSACTL_DELETE_DISK");
138 		status = aac_delete_disk(softs, arg, mode);
139 		break;
140 	case FSACTL_GET_FEATURES:
141 		AACDB_PRINT_IOCTL(softs, "FSACTL_GET_FEATURES");
142 		status = aac_supported_features(softs, arg, mode);
143 		break;
144 	default:
145 		status = ENOTTY;
146 		AACDB_PRINT(softs, CE_WARN,
147 		    "!IOCTL cmd 0x%x not supported", cmd);
148 		break;
149 	}
150 
151 	return (status);
152 }
153 
154 /*ARGSUSED*/
155 static int
156 aac_check_revision(struct aac_softstate *softs, intptr_t arg, int mode)
157 {
158 	union aac_revision_align un;
159 	struct aac_revision *aac_rev = &un.d;
160 
161 	DBCALLED(softs, 2);
162 
163 	/* Copyin the revision struct from userspace */
164 	if (ddi_copyin((void *)arg, aac_rev,
165 	    sizeof (struct aac_revision), mode) != 0)
166 		return (EFAULT);
167 
168 	/* Doctor up the response struct */
169 	aac_rev->compat = 1;
170 	aac_rev->version =
171 	    ((uint32_t)AAC_DRIVER_MAJOR_VERSION << 24) |
172 	    ((uint32_t)AAC_DRIVER_MINOR_VERSION << 16) |
173 	    ((uint32_t)AAC_DRIVER_TYPE << 8) |
174 	    ((uint32_t)AAC_DRIVER_BUGFIX_LEVEL);
175 	aac_rev->build = (uint32_t)AAC_DRIVER_BUILD;
176 
177 	if (ddi_copyout(aac_rev, (void *)arg,
178 	    sizeof (struct aac_revision), mode) != 0)
179 		return (EFAULT);
180 
181 	return (0);
182 }
183 
184 static int
185 aac_send_fib(struct aac_softstate *softs, struct aac_cmd *acp)
186 {
187 	int rval;
188 
189 	acp->flags |= AAC_CMD_NO_CB | AAC_CMD_SYNC;
190 	acp->ac_comp = aac_ioctl_complete;
191 
192 	mutex_enter(&softs->io_lock);
193 	if (softs->state & AAC_STATE_DEAD) {
194 		mutex_exit(&softs->io_lock);
195 		return (ENXIO);
196 	}
197 
198 	rval = aac_do_io(softs, acp);
199 	if (rval == TRAN_ACCEPT) {
200 		rval = 0;
201 	} else if (rval == TRAN_BADPKT) {
202 		AACDB_PRINT(softs, CE_CONT, "User SendFib failed ENXIO");
203 		rval = ENXIO;
204 	} else if (rval == TRAN_BUSY) {
205 		AACDB_PRINT(softs, CE_CONT, "User SendFib failed EBUSY");
206 		rval = EBUSY;
207 	}
208 	mutex_exit(&softs->io_lock);
209 
210 	return (rval);
211 }
212 
213 static int
214 aac_ioctl_send_fib(struct aac_softstate *softs, intptr_t arg, int mode)
215 {
216 	int hbalen;
217 	struct aac_cmd *acp;
218 	struct aac_fib *fibp;
219 	uint16_t fib_command;
220 	uint32_t fib_xfer_state;
221 	uint16_t fib_data_size, fib_size;
222 	uint16_t fib_sender_size;
223 	int rval;
224 
225 	DBCALLED(softs, 2);
226 
227 	/* Copy in FIB header */
228 	hbalen = sizeof (struct aac_cmd) + softs->aac_max_fib_size;
229 	if ((acp = kmem_zalloc(hbalen, KM_NOSLEEP)) == NULL)
230 		return (ENOMEM);
231 
232 	fibp = (struct aac_fib *)(acp + 1);
233 	acp->fibp = fibp;
234 	if (ddi_copyin((void *)arg, fibp,
235 	    sizeof (struct aac_fib_header), mode) != 0) {
236 		rval = EFAULT;
237 		goto finish;
238 	}
239 
240 	fib_xfer_state = LE_32(fibp->Header.XferState);
241 	fib_command = LE_16(fibp->Header.Command);
242 	fib_data_size = LE_16(fibp->Header.Size);
243 	fib_sender_size = LE_16(fibp->Header.SenderSize);
244 
245 	fib_size = fib_data_size + sizeof (struct aac_fib_header);
246 	if (fib_size < fib_sender_size)
247 		fib_size = fib_sender_size;
248 	if (fib_size > softs->aac_max_fib_size) {
249 		rval = EFAULT;
250 		goto finish;
251 	}
252 
253 	/* Copy in FIB data */
254 	if (ddi_copyin(((struct aac_fib *)arg)->data, fibp->data,
255 	    fib_data_size, mode) != 0) {
256 		rval = EFAULT;
257 		goto finish;
258 	}
259 	acp->fib_size = fib_size;
260 	fibp->Header.Size = LE_16(fib_size);
261 
262 	/* Process FIB */
263 	if (fib_command == TakeABreakPt) {
264 #ifdef DEBUG
265 		if (aac_dbflag_on(softs, AACDB_FLAGS_FIB) &&
266 		    (softs->debug_fib_flags & AACDB_FLAGS_FIB_IOCTL))
267 			aac_printf(softs, CE_NOTE, "FIB> TakeABreakPt, sz=%d",
268 			    fib_size);
269 #endif
270 		(void) aac_sync_mbcommand(softs, AAC_BREAKPOINT_REQ,
271 		    0, 0, 0, 0, NULL);
272 		fibp->Header.XferState = LE_32(0);
273 	} else {
274 		ASSERT(!(fib_xfer_state & AAC_FIBSTATE_ASYNC));
275 		fibp->Header.XferState = LE_32(fib_xfer_state | \
276 		    (AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_REXPECTED));
277 
278 		acp->timeout = AAC_IOCTL_TIMEOUT;
279 		acp->aac_cmd_fib = aac_cmd_fib_copy;
280 #ifdef DEBUG
281 		acp->fib_flags = AACDB_FLAGS_FIB_IOCTL;
282 #endif
283 		if ((rval = aac_send_fib(softs, acp)) != 0)
284 			goto finish;
285 	}
286 
287 	if (acp->flags & AAC_CMD_ERR) {
288 		AACDB_PRINT(softs, CE_CONT, "FIB data corrupt");
289 		rval = EIO;
290 		goto finish;
291 	}
292 
293 	if (ddi_copyout(fibp, (void *)arg, acp->fib_size, mode) != 0) {
294 		AACDB_PRINT(softs, CE_CONT, "FIB copyout failed");
295 		rval = EFAULT;
296 		goto finish;
297 	}
298 
299 	rval = 0;
300 finish:
301 	kmem_free(acp, hbalen);
302 	return (rval);
303 }
304 
305 static int
306 aac_open_getadapter_fib(struct aac_softstate *softs, intptr_t arg, int mode)
307 {
308 	struct aac_fib_context *fibctx, *ctx;
309 
310 	DBCALLED(softs, 2);
311 
312 	fibctx = kmem_zalloc(sizeof (struct aac_fib_context), KM_NOSLEEP);
313 	if (fibctx == NULL)
314 		return (ENOMEM);
315 
316 	mutex_enter(&softs->aifq_mutex);
317 	/* All elements are already 0, add to queue */
318 	if (softs->fibctx == NULL) {
319 		softs->fibctx = fibctx;
320 	} else {
321 		for (ctx = softs->fibctx; ctx->next; ctx = ctx->next)
322 			;
323 		ctx->next = fibctx;
324 		fibctx->prev = ctx;
325 	}
326 
327 	/* Evaluate unique value */
328 	fibctx->unique = (unsigned long)fibctx & 0xfffffffful;
329 	ctx = softs->fibctx;
330 	while (ctx != fibctx) {
331 		if (ctx->unique == fibctx->unique) {
332 			fibctx->unique++;
333 			ctx = softs->fibctx;
334 		} else {
335 			ctx = ctx->next;
336 		}
337 	}
338 
339 	/* Set ctx_idx to the oldest AIF */
340 	if (softs->aifq_wrap) {
341 		fibctx->ctx_idx = softs->aifq_idx;
342 		fibctx->ctx_filled = 1;
343 	}
344 	mutex_exit(&softs->aifq_mutex);
345 
346 	if (ddi_copyout(&fibctx->unique, (void *)arg,
347 	    sizeof (uint32_t), mode) != 0)
348 		return (EFAULT);
349 
350 	return (0);
351 }
352 
353 static int
354 aac_return_aif(struct aac_softstate *softs,
355     struct aac_fib_context *ctx, caddr_t uptr, int mode)
356 {
357 	int current;
358 
359 	current = ctx->ctx_idx;
360 	if (current == softs->aifq_idx && !ctx->ctx_filled)
361 		return (EAGAIN); /* Empty */
362 	if (ddi_copyout(&softs->aifq[current].d, (void *)uptr,
363 	    sizeof (struct aac_fib), mode) != 0)
364 		return (EFAULT);
365 
366 	ctx->ctx_filled = 0;
367 	ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH;
368 
369 	return (0);
370 }
371 
372 static int
373 aac_next_getadapter_fib(struct aac_softstate *softs, intptr_t arg, int mode)
374 {
375 	union aac_get_adapter_fib_align un;
376 	struct aac_get_adapter_fib *af = &un.d;
377 	struct aac_fib_context *ctx;
378 	int rval;
379 
380 	DBCALLED(softs, 2);
381 
382 	if (ddi_copyin((void *)arg, af, sizeof (*af), mode) != 0)
383 		return (EFAULT);
384 
385 	mutex_enter(&softs->aifq_mutex);
386 	for (ctx = softs->fibctx; ctx; ctx = ctx->next) {
387 		if (af->context == ctx->unique)
388 			break;
389 	}
390 	if (ctx) {
391 #ifdef	_LP64
392 		rval = aac_return_aif(softs, ctx,
393 		    (caddr_t)(uint64_t)af->aif_fib, mode);
394 #else
395 		rval = aac_return_aif(softs, ctx,
396 		    (caddr_t)af->aif_fib, mode);
397 #endif
398 		if (rval == EAGAIN && af->wait) {
399 			AACDB_PRINT(softs, CE_NOTE,
400 			    "aac_next_getadapter_fib(): waiting for AIF");
401 			rval = cv_wait_sig(&softs->aifv, &softs->aifq_mutex);
402 			if (rval > 0) {
403 #ifdef	_LP64
404 				rval = aac_return_aif(softs, ctx,
405 				    (caddr_t)(uint64_t)af->aif_fib, mode);
406 #else
407 				rval = aac_return_aif(softs, ctx,
408 				    (caddr_t)af->aif_fib, mode);
409 #endif
410 			} else {
411 				rval = EINTR;
412 			}
413 		}
414 	} else {
415 		rval = EFAULT;
416 	}
417 	mutex_exit(&softs->aifq_mutex);
418 
419 	return (rval);
420 }
421 
422 static int
423 aac_close_getadapter_fib(struct aac_softstate *softs, intptr_t arg)
424 {
425 	struct aac_fib_context *ctx;
426 
427 	DBCALLED(softs, 2);
428 
429 	mutex_enter(&softs->aifq_mutex);
430 	for (ctx = softs->fibctx; ctx; ctx = ctx->next) {
431 		if (ctx->unique != (uint32_t)arg)
432 			continue;
433 
434 		if (ctx == softs->fibctx)
435 			softs->fibctx = ctx->next;
436 		else
437 			ctx->prev->next = ctx->next;
438 		if (ctx->next)
439 			ctx->next->prev = ctx->prev;
440 		break;
441 	}
442 	mutex_exit(&softs->aifq_mutex);
443 	if (ctx)
444 		kmem_free(ctx, sizeof (struct aac_fib_context));
445 
446 	return (0);
447 }
448 
449 /*
450  * The following function comes from Adaptec:
451  *
452  * SRB is required for the new management tools
453  * Note: SRB passed down from IOCTL is always in CPU endianness.
454  */
455 static int
456 aac_send_raw_srb(struct aac_softstate *softs, dev_t dev, intptr_t arg, int mode)
457 {
458 	struct aac_cmd *acp;
459 	struct aac_fib *fibp;
460 	struct aac_srb *srb;
461 	uint32_t usr_fib_size;
462 	uint32_t srb_sgcount;
463 	struct aac_umem_sge *usgt = NULL;
464 	struct aac_umem_sge *usge;
465 	ddi_umem_cookie_t cookie;
466 	int umem_flags = 0;
467 	int direct = 0;
468 	int locked = 0;
469 	caddr_t addrlo = (caddr_t)-1;
470 	caddr_t addrhi = 0;
471 	struct aac_sge *sge, *sge0;
472 	int sg64;
473 	int rval;
474 
475 	DBCALLED(softs, 2);
476 
477 	/* Read srb size */
478 	if (ddi_copyin(&((struct aac_srb *)arg)->count, &usr_fib_size,
479 	    sizeof (uint32_t), mode) != 0)
480 		return (EFAULT);
481 	if (usr_fib_size > (softs->aac_max_fib_size - \
482 	    sizeof (struct aac_fib_header)))
483 		return (EINVAL);
484 
485 	if ((acp = kmem_zalloc(sizeof (struct aac_cmd) + usr_fib_size + \
486 	    sizeof (struct aac_fib_header), KM_NOSLEEP)) == NULL)
487 		return (ENOMEM);
488 
489 	acp->fibp = (struct aac_fib *)(acp + 1);
490 	fibp = acp->fibp;
491 	srb = (struct aac_srb *)fibp->data;
492 
493 	/* Copy in srb */
494 	if (ddi_copyin((void *)arg, srb, usr_fib_size, mode) != 0) {
495 		rval = EFAULT;
496 		goto finish;
497 	}
498 
499 	srb_sgcount = srb->sg.SgCount; /* No endianness conversion needed */
500 	if (srb_sgcount == 0)
501 		goto send_fib;
502 
503 	/* Check FIB size */
504 	if (usr_fib_size == (sizeof (struct aac_srb) + \
505 	    srb_sgcount * sizeof (struct aac_sg_entry64) - \
506 	    sizeof (struct aac_sg_entry))) {
507 		sg64 = 1;
508 	} else if (usr_fib_size == (sizeof (struct aac_srb) + \
509 	    (srb_sgcount - 1) * sizeof (struct aac_sg_entry))) {
510 		sg64 = 0;
511 	} else {
512 		rval = EINVAL;
513 		goto finish;
514 	}
515 
516 	/* Read user SG table */
517 	if ((usgt = kmem_zalloc(sizeof (struct aac_umem_sge) * srb_sgcount,
518 	    KM_NOSLEEP)) == NULL) {
519 		rval = ENOMEM;
520 		goto finish;
521 	}
522 	for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
523 		if (sg64) {
524 			struct aac_sg_entry64 *sg64p =
525 			    (struct aac_sg_entry64 *)srb->sg.SgEntry;
526 
527 			usge->bcount = sg64p->SgByteCount;
528 			usge->addr = (caddr_t)
529 #ifndef _LP64
530 			    (uint32_t)
531 #endif
532 			    sg64p->SgAddress;
533 		} else {
534 			struct aac_sg_entry *sgp = srb->sg.SgEntry;
535 
536 			usge->bcount = sgp->SgByteCount;
537 			usge->addr = (caddr_t)
538 #ifdef _LP64
539 			    (uint64_t)
540 #endif
541 			    sgp->SgAddress;
542 		}
543 		acp->bcount += usge->bcount;
544 		if (usge->addr < addrlo)
545 			addrlo = usge->addr;
546 		if ((usge->addr + usge->bcount) > addrhi)
547 			addrhi = usge->addr + usge->bcount;
548 	}
549 	if (acp->bcount > softs->buf_dma_attr.dma_attr_maxxfer) {
550 		AACDB_PRINT(softs, CE_NOTE,
551 		    "large srb xfer size received %d\n", acp->bcount);
552 		rval = EINVAL;
553 		goto finish;
554 	}
555 
556 	/* Lock user buffers */
557 	if (srb->flags & SRB_DataIn) {
558 		umem_flags |= DDI_UMEMLOCK_READ;
559 		direct |= B_READ;
560 	}
561 	if (srb->flags & SRB_DataOut) {
562 		umem_flags |= DDI_UMEMLOCK_WRITE;
563 		direct |= B_WRITE;
564 	}
565 	addrlo = (caddr_t)((uintptr_t)addrlo & (uintptr_t)PAGEMASK);
566 	rval = ddi_umem_lock(addrlo, (((size_t)addrhi + PAGEOFFSET) & \
567 	    PAGEMASK) - (size_t)addrlo, umem_flags, &cookie);
568 	if (rval != 0) {
569 		AACDB_PRINT(softs, CE_NOTE, "ddi_umem_lock failed: %d",
570 		    rval);
571 		goto finish;
572 	}
573 	locked = 1;
574 
575 	/* Allocate DMA for user buffers */
576 	for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
577 		struct buf *bp;
578 
579 		bp = ddi_umem_iosetup(cookie, (uintptr_t)usge->addr - \
580 		    (uintptr_t)addrlo, usge->bcount, direct, dev, 0, NULL,
581 		    DDI_UMEM_NOSLEEP);
582 		if (bp == NULL) {
583 			AACDB_PRINT(softs, CE_NOTE, "ddi_umem_iosetup failed");
584 			rval = ENOMEM;
585 			goto finish;
586 		}
587 		if (aac_cmd_dma_alloc(softs, &usge->acp, bp, 0, NULL_FUNC,
588 		    0) != AACOK) {
589 			rval = EFAULT;
590 			goto finish;
591 		}
592 		acp->left_cookien += usge->acp.left_cookien;
593 		if (acp->left_cookien > softs->aac_sg_tablesize) {
594 			AACDB_PRINT(softs, CE_NOTE, "large cookiec received %d",
595 			    acp->left_cookien);
596 			rval = EINVAL;
597 			goto finish;
598 		}
599 	}
600 
601 	/* Construct aac cmd SG table */
602 	if ((sge = kmem_zalloc(sizeof (struct aac_sge) * acp->left_cookien,
603 	    KM_NOSLEEP)) == NULL) {
604 		rval = ENOMEM;
605 		goto finish;
606 	}
607 	acp->sgt = sge;
608 	for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
609 		for (sge0 = usge->acp.sgt;
610 		    sge0 < &usge->acp.sgt[usge->acp.left_cookien];
611 		    sge0++, sge++)
612 			*sge = *sge0;
613 	}
614 
615 send_fib:
616 	acp->cmdlen = srb->cdb_size;
617 	acp->timeout = srb->timeout;
618 
619 	/* Send FIB command */
620 	acp->aac_cmd_fib = softs->aac_cmd_fib_scsi;
621 #ifdef DEBUG
622 	acp->fib_flags = AACDB_FLAGS_FIB_SRB;
623 #endif
624 	if ((rval = aac_send_fib(softs, acp)) != 0)
625 		goto finish;
626 
627 	/* Status struct */
628 	if (ddi_copyout((struct aac_srb_reply *)fibp->data,
629 	    ((uint8_t *)arg + usr_fib_size),
630 	    sizeof (struct aac_srb_reply), mode) != 0) {
631 		rval = EFAULT;
632 		goto finish;
633 	}
634 
635 	rval = 0;
636 finish:
637 	if (acp->sgt)
638 		kmem_free(acp->sgt, sizeof (struct aac_sge) * \
639 		    acp->left_cookien);
640 	if (usgt) {
641 		for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
642 			if (usge->acp.sgt)
643 				kmem_free(usge->acp.sgt,
644 				    sizeof (struct aac_sge) * \
645 				    usge->acp.left_cookien);
646 			aac_free_dmamap(&usge->acp);
647 			if (usge->acp.bp)
648 				freerbuf(usge->acp.bp);
649 		}
650 		kmem_free(usgt, sizeof (struct aac_umem_sge) * srb_sgcount);
651 	}
652 	if (locked)
653 		ddi_umem_unlock(cookie);
654 	kmem_free(acp, sizeof (struct aac_cmd) + usr_fib_size + \
655 	    sizeof (struct aac_fib_header));
656 	return (rval);
657 }
658 
659 /*ARGSUSED*/
660 static int
661 aac_get_pci_info(struct aac_softstate *softs, intptr_t arg, int mode)
662 {
663 	union aac_pci_info_align un;
664 	struct aac_pci_info *resp = &un.d;
665 
666 	DBCALLED(softs, 2);
667 
668 	resp->bus = 0;
669 	resp->slot = 0;
670 
671 	if (ddi_copyout(resp, (void *)arg,
672 	    sizeof (struct aac_pci_info), mode) != 0)
673 		return (EFAULT);
674 	return (0);
675 }
676 
677 static int
678 aac_query_disk(struct aac_softstate *softs, intptr_t arg, int mode)
679 {
680 	union aac_query_disk_align un;
681 	struct aac_query_disk *qdisk = &un.d;
682 	struct aac_container *dvp;
683 
684 	DBCALLED(softs, 2);
685 
686 	if (ddi_copyin((void *)arg, qdisk, sizeof (*qdisk), mode) != 0)
687 		return (EFAULT);
688 
689 	if (qdisk->container_no == -1) {
690 		qdisk->container_no = qdisk->target * 16 + qdisk->lun;
691 	} else if (qdisk->bus == -1 && qdisk->target == -1 &&
692 	    qdisk->lun == -1) {
693 		if (qdisk->container_no >= AAC_MAX_CONTAINERS)
694 			return (EINVAL);
695 		qdisk->bus = 0;
696 		qdisk->target = (qdisk->container_no & 0xf);
697 		qdisk->lun = (qdisk->container_no >> 4);
698 	} else {
699 		return (EINVAL);
700 	}
701 
702 	mutex_enter(&softs->io_lock);
703 	dvp = &softs->containers[qdisk->container_no];
704 	qdisk->valid = AAC_DEV_IS_VALID(&dvp->dev);
705 	qdisk->locked = dvp->locked;
706 	qdisk->deleted = dvp->deleted;
707 	mutex_exit(&softs->io_lock);
708 
709 	if (ddi_copyout(qdisk, (void *)arg, sizeof (*qdisk), mode) != 0)
710 		return (EFAULT);
711 	return (0);
712 }
713 
714 static int
715 aac_delete_disk(struct aac_softstate *softs, intptr_t arg, int mode)
716 {
717 	union aac_delete_disk_align un;
718 	struct aac_delete_disk *ddisk = &un.d;
719 	struct aac_container *dvp;
720 	int rval = 0;
721 
722 	DBCALLED(softs, 2);
723 
724 	if (ddi_copyin((void *)arg, ddisk, sizeof (*ddisk), mode) != 0)
725 		return (EFAULT);
726 
727 	if (ddisk->container_no >= AAC_MAX_CONTAINERS)
728 		return (EINVAL);
729 
730 	mutex_enter(&softs->io_lock);
731 	dvp = &softs->containers[ddisk->container_no];
732 	/*
733 	 * We don't trust the userland to tell us when to delete
734 	 * a container, rather we rely on an AIF coming from the
735 	 * controller.
736 	 */
737 	if (AAC_DEV_IS_VALID(&dvp->dev)) {
738 		if (dvp->locked)
739 			rval = EBUSY;
740 	}
741 	mutex_exit(&softs->io_lock);
742 
743 	return (rval);
744 }
745 
746 /*
747  * The following function comes from Adaptec to support creation of arrays
748  * bigger than 2TB.
749  */
750 static int
751 aac_supported_features(struct aac_softstate *softs, intptr_t arg, int mode)
752 {
753 	union aac_features_align un;
754 	struct aac_features *f = &un.d;
755 
756 	DBCALLED(softs, 2);
757 
758 	if (ddi_copyin((void *)arg, f, sizeof (*f), mode) != 0)
759 		return (EFAULT);
760 
761 	/*
762 	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
763 	 * ALL zero in the featuresState, the driver will return the current
764 	 * state of all the supported features, the data field will not be
765 	 * valid.
766 	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
767 	 * a specific bit set in the featuresState, the driver will return the
768 	 * current state of this specific feature and whatever data that are
769 	 * associated with the feature in the data field or perform whatever
770 	 * action needed indicates in the data field.
771 	 */
772 	if (f->feat.fValue == 0) {
773 		f->feat.fBits.largeLBA =
774 		    (softs->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
775 		/* TODO: In the future, add other features state here as well */
776 	} else {
777 		if (f->feat.fBits.largeLBA)
778 			f->feat.fBits.largeLBA =
779 			    (softs->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
780 		/* TODO: Add other features state and data in the future */
781 	}
782 
783 	if (ddi_copyout(f, (void *)arg, sizeof (*f), mode) != 0)
784 		return (EFAULT);
785 	return (0);
786 }
787