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