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