xref: /onnv-gate/usr/src/uts/common/io/aac/aac_ioctl.c (revision 11348:2c50f5fea0ba)
15678Spl196000 /*
2*11348SZhongyan.Gu@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
35678Spl196000  * Use is subject to license terms.
45678Spl196000  */
55678Spl196000 
65678Spl196000 /*
75678Spl196000  * Copyright 2005-06 Adaptec, Inc.
85678Spl196000  * Copyright (c) 2005-06 Adaptec Inc., Achim Leubner
95678Spl196000  * Copyright (c) 2000 Michael Smith
105678Spl196000  * Copyright (c) 2001 Scott Long
115678Spl196000  * Copyright (c) 2000 BSDi
125678Spl196000  * All rights reserved.
135678Spl196000  *
145678Spl196000  * Redistribution and use in source and binary forms, with or without
155678Spl196000  * modification, are permitted provided that the following conditions
165678Spl196000  * are met:
175678Spl196000  * 1. Redistributions of source code must retain the above copyright
185678Spl196000  *    notice, this list of conditions and the following disclaimer.
195678Spl196000  * 2. Redistributions in binary form must reproduce the above copyright
205678Spl196000  *    notice, this list of conditions and the following disclaimer in the
215678Spl196000  *    documentation and/or other materials provided with the distribution.
225678Spl196000  *
235678Spl196000  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
245678Spl196000  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
255678Spl196000  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
265678Spl196000  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
275678Spl196000  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
285678Spl196000  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
295678Spl196000  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
305678Spl196000  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
315678Spl196000  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
325678Spl196000  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
335678Spl196000  * SUCH DAMAGE.
345678Spl196000  */
355678Spl196000 #include <sys/modctl.h>
365678Spl196000 #include <sys/conf.h>
375678Spl196000 #include <sys/cmn_err.h>
385678Spl196000 #include <sys/ddi.h>
395678Spl196000 #include <sys/devops.h>
405678Spl196000 #include <sys/pci.h>
415678Spl196000 #include <sys/types.h>
425678Spl196000 #include <sys/ddidmareq.h>
435678Spl196000 #include <sys/scsi/scsi.h>
445678Spl196000 #include <sys/ksynch.h>
455678Spl196000 #include <sys/sunddi.h>
465678Spl196000 #include <sys/byteorder.h>
475678Spl196000 #include <sys/kmem.h>
485678Spl196000 #include "aac_regs.h"
495678Spl196000 #include "aac.h"
505678Spl196000 #include "aac_ioctl.h"
515678Spl196000 
525678Spl196000 struct aac_umem_sge {
535678Spl196000 	uint32_t bcount;
545678Spl196000 	caddr_t addr;
555678Spl196000 	struct aac_cmd acp;
565678Spl196000 };
575678Spl196000 
585678Spl196000 /*
595678Spl196000  * External functions
605678Spl196000  */
615678Spl196000 extern int aac_sync_mbcommand(struct aac_softstate *, uint32_t, uint32_t,
625678Spl196000     uint32_t, uint32_t, uint32_t, uint32_t *);
635678Spl196000 extern int aac_cmd_dma_alloc(struct aac_softstate *, struct aac_cmd *,
645678Spl196000     struct buf *, int, int (*)(), caddr_t);
655678Spl196000 extern void aac_free_dmamap(struct aac_cmd *);
665678Spl196000 extern int aac_do_io(struct aac_softstate *, struct aac_cmd *);
675678Spl196000 extern void aac_cmd_fib_copy(struct aac_softstate *, struct aac_cmd *);
685678Spl196000 extern void aac_ioctl_complete(struct aac_softstate *, struct aac_cmd *);
695678Spl196000 
705678Spl196000 extern ddi_device_acc_attr_t aac_acc_attr;
715678Spl196000 extern int aac_check_dma_handle(ddi_dma_handle_t);
725678Spl196000 
735678Spl196000 /*
745678Spl196000  * IOCTL command handling functions
755678Spl196000  */
765678Spl196000 static int aac_check_revision(struct aac_softstate *, intptr_t, int);
775678Spl196000 static int aac_ioctl_send_fib(struct aac_softstate *, intptr_t, int);
785678Spl196000 static int aac_open_getadapter_fib(struct aac_softstate *, intptr_t, int);
795678Spl196000 static int aac_next_getadapter_fib(struct aac_softstate *, intptr_t, int);
805678Spl196000 static int aac_close_getadapter_fib(struct aac_softstate *, intptr_t);
815678Spl196000 static int aac_send_raw_srb(struct aac_softstate *, dev_t, intptr_t, int);
825678Spl196000 static int aac_get_pci_info(struct aac_softstate *, intptr_t, int);
835678Spl196000 static int aac_query_disk(struct aac_softstate *, intptr_t, int);
845678Spl196000 static int aac_delete_disk(struct aac_softstate *, intptr_t, int);
855678Spl196000 static int aac_supported_features(struct aac_softstate *, intptr_t, int);
865678Spl196000 
875678Spl196000 /*
885678Spl196000  * Warlock directives
895678Spl196000  */
905678Spl196000 _NOTE(SCHEME_PROTECTS_DATA("unique to each handling function", aac_features
915678Spl196000     aac_pci_info aac_query_disk aac_revision aac_umem_sge))
925678Spl196000 
935678Spl196000 int
945678Spl196000 aac_do_ioctl(struct aac_softstate *softs, dev_t dev, int cmd, intptr_t arg,
955678Spl196000     int mode)
965678Spl196000 {
975678Spl196000 	int status;
985678Spl196000 
995678Spl196000 	switch (cmd) {
1005678Spl196000 	case FSACTL_MINIPORT_REV_CHECK:
1015678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_MINIPORT_REV_CHECK");
1025678Spl196000 		status = aac_check_revision(softs, arg, mode);
1035678Spl196000 		break;
1045678Spl196000 	case FSACTL_SENDFIB:
1055678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_LARGE_FIB");
1065678Spl196000 		goto send_fib;
1075678Spl196000 	case FSACTL_SEND_LARGE_FIB:
1085678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_LARGE_FIB");
1095678Spl196000 send_fib:
1105678Spl196000 		status = aac_ioctl_send_fib(softs, arg, mode);
1115678Spl196000 		break;
1125678Spl196000 	case FSACTL_OPEN_GET_ADAPTER_FIB:
1135678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_OPEN_GET_ADAPTER_FIB");
1145678Spl196000 		status = aac_open_getadapter_fib(softs, arg, mode);
1155678Spl196000 		break;
1165678Spl196000 	case FSACTL_GET_NEXT_ADAPTER_FIB:
1175678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_GET_NEXT_ADAPTER_FIB");
1185678Spl196000 		status = aac_next_getadapter_fib(softs, arg, mode);
1195678Spl196000 		break;
1205678Spl196000 	case FSACTL_CLOSE_GET_ADAPTER_FIB:
1215678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_CLOSE_GET_ADAPTER_FIB");
1225678Spl196000 		status = aac_close_getadapter_fib(softs, arg);
1235678Spl196000 		break;
1245678Spl196000 	case FSACTL_SEND_RAW_SRB:
1255678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_RAW_SRB");
1265678Spl196000 		status = aac_send_raw_srb(softs, dev, arg, mode);
1275678Spl196000 		break;
1285678Spl196000 	case FSACTL_GET_PCI_INFO:
1295678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_GET_PCI_INFO");
1305678Spl196000 		status = aac_get_pci_info(softs, arg, mode);
1315678Spl196000 		break;
1325678Spl196000 	case FSACTL_QUERY_DISK:
1335678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_QUERY_DISK");
1345678Spl196000 		status = aac_query_disk(softs, arg, mode);
1355678Spl196000 		break;
1365678Spl196000 	case FSACTL_DELETE_DISK:
1375678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_DELETE_DISK");
1385678Spl196000 		status = aac_delete_disk(softs, arg, mode);
1395678Spl196000 		break;
1405678Spl196000 	case FSACTL_GET_FEATURES:
1415678Spl196000 		AACDB_PRINT_IOCTL(softs, "FSACTL_GET_FEATURES");
1425678Spl196000 		status = aac_supported_features(softs, arg, mode);
1435678Spl196000 		break;
1445678Spl196000 	default:
1455678Spl196000 		status = ENOTTY;
1465678Spl196000 		AACDB_PRINT(softs, CE_WARN,
1475678Spl196000 		    "!IOCTL cmd 0x%x not supported", cmd);
1485678Spl196000 		break;
1495678Spl196000 	}
1505678Spl196000 
1515678Spl196000 	return (status);
1525678Spl196000 }
1535678Spl196000 
1545678Spl196000 /*ARGSUSED*/
1555678Spl196000 static int
1565678Spl196000 aac_check_revision(struct aac_softstate *softs, intptr_t arg, int mode)
1575678Spl196000 {
1585678Spl196000 	union aac_revision_align un;
1595678Spl196000 	struct aac_revision *aac_rev = &un.d;
1605678Spl196000 
1615678Spl196000 	DBCALLED(softs, 2);
1625678Spl196000 
1635678Spl196000 	/* Copyin the revision struct from userspace */
1645678Spl196000 	if (ddi_copyin((void *)arg, aac_rev,
1655678Spl196000 	    sizeof (struct aac_revision), mode) != 0)
1665678Spl196000 		return (EFAULT);
1675678Spl196000 
1685678Spl196000 	/* Doctor up the response struct */
1695678Spl196000 	aac_rev->compat = 1;
1705678Spl196000 	aac_rev->version =
1715678Spl196000 	    ((uint32_t)AAC_DRIVER_MAJOR_VERSION << 24) |
1725678Spl196000 	    ((uint32_t)AAC_DRIVER_MINOR_VERSION << 16) |
1735678Spl196000 	    ((uint32_t)AAC_DRIVER_TYPE << 8) |
1745678Spl196000 	    ((uint32_t)AAC_DRIVER_BUGFIX_LEVEL);
1755678Spl196000 	aac_rev->build = (uint32_t)AAC_DRIVER_BUILD;
1765678Spl196000 
1775678Spl196000 	if (ddi_copyout(aac_rev, (void *)arg,
1785678Spl196000 	    sizeof (struct aac_revision), mode) != 0)
1795678Spl196000 		return (EFAULT);
1805678Spl196000 
1815678Spl196000 	return (0);
1825678Spl196000 }
1835678Spl196000 
1845678Spl196000 static int
1855678Spl196000 aac_send_fib(struct aac_softstate *softs, struct aac_cmd *acp)
1865678Spl196000 {
1875678Spl196000 	int rval;
1885678Spl196000 
1895678Spl196000 	acp->flags |= AAC_CMD_NO_CB | AAC_CMD_SYNC;
1905678Spl196000 	acp->ac_comp = aac_ioctl_complete;
1915678Spl196000 
1925678Spl196000 	mutex_enter(&softs->io_lock);
1935678Spl196000 	if (softs->state & AAC_STATE_DEAD) {
1945678Spl196000 		mutex_exit(&softs->io_lock);
1955678Spl196000 		return (ENXIO);
1965678Spl196000 	}
1975678Spl196000 
1985678Spl196000 	rval = aac_do_io(softs, acp);
1995678Spl196000 	if (rval == TRAN_ACCEPT) {
2005678Spl196000 		rval = 0;
2015678Spl196000 	} else if (rval == TRAN_BADPKT) {
2025678Spl196000 		AACDB_PRINT(softs, CE_CONT, "User SendFib failed ENXIO");
2035678Spl196000 		rval = ENXIO;
2045678Spl196000 	} else if (rval == TRAN_BUSY) {
2055678Spl196000 		AACDB_PRINT(softs, CE_CONT, "User SendFib failed EBUSY");
2065678Spl196000 		rval = EBUSY;
2075678Spl196000 	}
2085678Spl196000 	mutex_exit(&softs->io_lock);
2095678Spl196000 
2105678Spl196000 	return (rval);
2115678Spl196000 }
2125678Spl196000 
2135678Spl196000 static int
2145678Spl196000 aac_ioctl_send_fib(struct aac_softstate *softs, intptr_t arg, int mode)
2155678Spl196000 {
2165678Spl196000 	int hbalen;
2175678Spl196000 	struct aac_cmd *acp;
2185678Spl196000 	struct aac_fib *fibp;
2195678Spl196000 	uint16_t fib_command;
2205678Spl196000 	uint32_t fib_xfer_state;
2215678Spl196000 	uint16_t fib_data_size, fib_size;
2225678Spl196000 	uint16_t fib_sender_size;
2235678Spl196000 	int rval;
2245678Spl196000 
2255678Spl196000 	DBCALLED(softs, 2);
2265678Spl196000 
2275678Spl196000 	/* Copy in FIB header */
2285678Spl196000 	hbalen = sizeof (struct aac_cmd) + softs->aac_max_fib_size;
2295678Spl196000 	if ((acp = kmem_zalloc(hbalen, KM_NOSLEEP)) == NULL)
2305678Spl196000 		return (ENOMEM);
2315678Spl196000 
2325678Spl196000 	fibp = (struct aac_fib *)(acp + 1);
2335678Spl196000 	acp->fibp = fibp;
2345678Spl196000 	if (ddi_copyin((void *)arg, fibp,
2355678Spl196000 	    sizeof (struct aac_fib_header), mode) != 0) {
2365678Spl196000 		rval = EFAULT;
2375678Spl196000 		goto finish;
2385678Spl196000 	}
2395678Spl196000 
2405678Spl196000 	fib_xfer_state = LE_32(fibp->Header.XferState);
2415678Spl196000 	fib_command = LE_16(fibp->Header.Command);
2425678Spl196000 	fib_data_size = LE_16(fibp->Header.Size);
2435678Spl196000 	fib_sender_size = LE_16(fibp->Header.SenderSize);
2445678Spl196000 
2455678Spl196000 	fib_size = fib_data_size + sizeof (struct aac_fib_header);
2465678Spl196000 	if (fib_size < fib_sender_size)
2475678Spl196000 		fib_size = fib_sender_size;
2485678Spl196000 	if (fib_size > softs->aac_max_fib_size) {
2495678Spl196000 		rval = EFAULT;
2505678Spl196000 		goto finish;
2515678Spl196000 	}
2525678Spl196000 
2535678Spl196000 	/* Copy in FIB data */
2545678Spl196000 	if (ddi_copyin(((struct aac_fib *)arg)->data, fibp->data,
2555678Spl196000 	    fib_data_size, mode) != 0) {
2565678Spl196000 		rval = EFAULT;
2575678Spl196000 		goto finish;
2585678Spl196000 	}
2595678Spl196000 	acp->fib_size = fib_size;
2605678Spl196000 	fibp->Header.Size = LE_16(fib_size);
2615678Spl196000 
2625678Spl196000 	/* Process FIB */
2635678Spl196000 	if (fib_command == TakeABreakPt) {
2647567SXin.Chen@Sun.COM #ifdef DEBUG
2657567SXin.Chen@Sun.COM 		if (aac_dbflag_on(softs, AACDB_FLAGS_FIB) &&
2667567SXin.Chen@Sun.COM 		    (softs->debug_fib_flags & AACDB_FLAGS_FIB_IOCTL))
2677567SXin.Chen@Sun.COM 			aac_printf(softs, CE_NOTE, "FIB> TakeABreakPt, sz=%d",
2687567SXin.Chen@Sun.COM 			    fib_size);
2697567SXin.Chen@Sun.COM #endif
2705678Spl196000 		(void) aac_sync_mbcommand(softs, AAC_BREAKPOINT_REQ,
2715678Spl196000 		    0, 0, 0, 0, NULL);
2725678Spl196000 		fibp->Header.XferState = LE_32(0);
2735678Spl196000 	} else {
2745678Spl196000 		ASSERT(!(fib_xfer_state & AAC_FIBSTATE_ASYNC));
2755678Spl196000 		fibp->Header.XferState = LE_32(fib_xfer_state | \
2765678Spl196000 		    (AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_REXPECTED));
2775678Spl196000 
2785678Spl196000 		acp->timeout = AAC_IOCTL_TIMEOUT;
2795678Spl196000 		acp->aac_cmd_fib = aac_cmd_fib_copy;
2807567SXin.Chen@Sun.COM #ifdef DEBUG
2817567SXin.Chen@Sun.COM 		acp->fib_flags = AACDB_FLAGS_FIB_IOCTL;
2827567SXin.Chen@Sun.COM #endif
2835678Spl196000 		if ((rval = aac_send_fib(softs, acp)) != 0)
2845678Spl196000 			goto finish;
2855678Spl196000 	}
2865678Spl196000 
2875678Spl196000 	if (acp->flags & AAC_CMD_ERR) {
2885678Spl196000 		AACDB_PRINT(softs, CE_CONT, "FIB data corrupt");
2895678Spl196000 		rval = EIO;
2905678Spl196000 		goto finish;
2915678Spl196000 	}
2925678Spl196000 
2935678Spl196000 	if (ddi_copyout(fibp, (void *)arg, acp->fib_size, mode) != 0) {
2945678Spl196000 		AACDB_PRINT(softs, CE_CONT, "FIB copyout failed");
2955678Spl196000 		rval = EFAULT;
2965678Spl196000 		goto finish;
2975678Spl196000 	}
2985678Spl196000 
2995678Spl196000 	rval = 0;
3005678Spl196000 finish:
3015678Spl196000 	kmem_free(acp, hbalen);
3025678Spl196000 	return (rval);
3035678Spl196000 }
3045678Spl196000 
3055678Spl196000 static int
3065678Spl196000 aac_open_getadapter_fib(struct aac_softstate *softs, intptr_t arg, int mode)
3075678Spl196000 {
3085678Spl196000 	struct aac_fib_context *fibctx, *ctx;
3095678Spl196000 
3105678Spl196000 	DBCALLED(softs, 2);
3115678Spl196000 
3125678Spl196000 	fibctx = kmem_zalloc(sizeof (struct aac_fib_context), KM_NOSLEEP);
3135678Spl196000 	if (fibctx == NULL)
3145678Spl196000 		return (ENOMEM);
3155678Spl196000 
3165678Spl196000 	mutex_enter(&softs->aifq_mutex);
3175678Spl196000 	/* All elements are already 0, add to queue */
3185678Spl196000 	if (softs->fibctx == NULL) {
3195678Spl196000 		softs->fibctx = fibctx;
3205678Spl196000 	} else {
3215678Spl196000 		for (ctx = softs->fibctx; ctx->next; ctx = ctx->next)
3225678Spl196000 			;
3235678Spl196000 		ctx->next = fibctx;
3245678Spl196000 		fibctx->prev = ctx;
3255678Spl196000 	}
3265678Spl196000 
3275678Spl196000 	/* Evaluate unique value */
3285678Spl196000 	fibctx->unique = (unsigned long)fibctx & 0xfffffffful;
3295678Spl196000 	ctx = softs->fibctx;
3305678Spl196000 	while (ctx != fibctx) {
3315678Spl196000 		if (ctx->unique == fibctx->unique) {
3325678Spl196000 			fibctx->unique++;
3335678Spl196000 			ctx = softs->fibctx;
3345678Spl196000 		} else {
3355678Spl196000 			ctx = ctx->next;
3365678Spl196000 		}
3375678Spl196000 	}
3385678Spl196000 
3395678Spl196000 	/* Set ctx_idx to the oldest AIF */
3405678Spl196000 	if (softs->aifq_wrap) {
3415678Spl196000 		fibctx->ctx_idx = softs->aifq_idx;
3425678Spl196000 		fibctx->ctx_filled = 1;
3435678Spl196000 	}
3445678Spl196000 	mutex_exit(&softs->aifq_mutex);
3455678Spl196000 
3465678Spl196000 	if (ddi_copyout(&fibctx->unique, (void *)arg,
3475678Spl196000 	    sizeof (uint32_t), mode) != 0)
3485678Spl196000 		return (EFAULT);
3495678Spl196000 
3505678Spl196000 	return (0);
3515678Spl196000 }
3525678Spl196000 
3535678Spl196000 static int
3545678Spl196000 aac_return_aif(struct aac_softstate *softs,
3555678Spl196000     struct aac_fib_context *ctx, caddr_t uptr, int mode)
3565678Spl196000 {
3575678Spl196000 	int current;
3585678Spl196000 
3595678Spl196000 	current = ctx->ctx_idx;
3605678Spl196000 	if (current == softs->aifq_idx && !ctx->ctx_filled)
3615678Spl196000 		return (EAGAIN); /* Empty */
3625678Spl196000 	if (ddi_copyout(&softs->aifq[current].d, (void *)uptr,
3635678Spl196000 	    sizeof (struct aac_fib), mode) != 0)
3645678Spl196000 		return (EFAULT);
3655678Spl196000 
3665678Spl196000 	ctx->ctx_filled = 0;
3675678Spl196000 	ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH;
3685678Spl196000 
3695678Spl196000 	return (0);
3705678Spl196000 }
3715678Spl196000 
3725678Spl196000 static int
3735678Spl196000 aac_next_getadapter_fib(struct aac_softstate *softs, intptr_t arg, int mode)
3745678Spl196000 {
3755678Spl196000 	union aac_get_adapter_fib_align un;
3765678Spl196000 	struct aac_get_adapter_fib *af = &un.d;
3775678Spl196000 	struct aac_fib_context *ctx;
3785678Spl196000 	int rval;
3795678Spl196000 
3805678Spl196000 	DBCALLED(softs, 2);
3815678Spl196000 
3825678Spl196000 	if (ddi_copyin((void *)arg, af, sizeof (*af), mode) != 0)
3835678Spl196000 		return (EFAULT);
3845678Spl196000 
3855678Spl196000 	mutex_enter(&softs->aifq_mutex);
3865678Spl196000 	for (ctx = softs->fibctx; ctx; ctx = ctx->next) {
3875678Spl196000 		if (af->context == ctx->unique)
3885678Spl196000 			break;
3895678Spl196000 	}
3905678Spl196000 	if (ctx) {
3915678Spl196000 #ifdef	_LP64
3925678Spl196000 		rval = aac_return_aif(softs, ctx,
3935678Spl196000 		    (caddr_t)(uint64_t)af->aif_fib, mode);
3945678Spl196000 #else
3955678Spl196000 		rval = aac_return_aif(softs, ctx,
3965678Spl196000 		    (caddr_t)af->aif_fib, mode);
3975678Spl196000 #endif
3985678Spl196000 		if (rval == EAGAIN && af->wait) {
3995678Spl196000 			AACDB_PRINT(softs, CE_NOTE,
4005678Spl196000 			    "aac_next_getadapter_fib(): waiting for AIF");
4015678Spl196000 			rval = cv_wait_sig(&softs->aifv, &softs->aifq_mutex);
4025678Spl196000 			if (rval > 0) {
4035678Spl196000 #ifdef	_LP64
4045678Spl196000 				rval = aac_return_aif(softs, ctx,
4055678Spl196000 				    (caddr_t)(uint64_t)af->aif_fib, mode);
4065678Spl196000 #else
4075678Spl196000 				rval = aac_return_aif(softs, ctx,
4085678Spl196000 				    (caddr_t)af->aif_fib, mode);
4095678Spl196000 #endif
4105678Spl196000 			} else {
4115678Spl196000 				rval = EINTR;
4125678Spl196000 			}
4135678Spl196000 		}
4145678Spl196000 	} else {
4155678Spl196000 		rval = EFAULT;
4165678Spl196000 	}
4175678Spl196000 	mutex_exit(&softs->aifq_mutex);
4185678Spl196000 
4195678Spl196000 	return (rval);
4205678Spl196000 }
4215678Spl196000 
4225678Spl196000 static int
4235678Spl196000 aac_close_getadapter_fib(struct aac_softstate *softs, intptr_t arg)
4245678Spl196000 {
4255678Spl196000 	struct aac_fib_context *ctx;
4265678Spl196000 
4275678Spl196000 	DBCALLED(softs, 2);
4285678Spl196000 
4295678Spl196000 	mutex_enter(&softs->aifq_mutex);
4305678Spl196000 	for (ctx = softs->fibctx; ctx; ctx = ctx->next) {
4315678Spl196000 		if (ctx->unique != (uint32_t)arg)
4325678Spl196000 			continue;
4335678Spl196000 
4345678Spl196000 		if (ctx == softs->fibctx)
4355678Spl196000 			softs->fibctx = ctx->next;
4365678Spl196000 		else
4375678Spl196000 			ctx->prev->next = ctx->next;
4385678Spl196000 		if (ctx->next)
4395678Spl196000 			ctx->next->prev = ctx->prev;
4405678Spl196000 		break;
4415678Spl196000 	}
4425678Spl196000 	mutex_exit(&softs->aifq_mutex);
4435678Spl196000 	if (ctx)
4445678Spl196000 		kmem_free(ctx, sizeof (struct aac_fib_context));
4455678Spl196000 
4465678Spl196000 	return (0);
4475678Spl196000 }
4485678Spl196000 
4495678Spl196000 /*
4505678Spl196000  * The following function comes from Adaptec:
4515678Spl196000  *
4525678Spl196000  * SRB is required for the new management tools
4535678Spl196000  * Note: SRB passed down from IOCTL is always in CPU endianness.
4545678Spl196000  */
4555678Spl196000 static int
4565678Spl196000 aac_send_raw_srb(struct aac_softstate *softs, dev_t dev, intptr_t arg, int mode)
4575678Spl196000 {
4585678Spl196000 	struct aac_cmd *acp;
4595678Spl196000 	struct aac_fib *fibp;
4605678Spl196000 	struct aac_srb *srb;
4615678Spl196000 	uint32_t usr_fib_size;
4625678Spl196000 	uint32_t srb_sgcount;
4635678Spl196000 	struct aac_umem_sge *usgt = NULL;
4645678Spl196000 	struct aac_umem_sge *usge;
4655678Spl196000 	ddi_umem_cookie_t cookie;
4665678Spl196000 	int umem_flags = 0;
4675678Spl196000 	int direct = 0;
4685678Spl196000 	int locked = 0;
4695678Spl196000 	caddr_t addrlo = (caddr_t)-1;
4705678Spl196000 	caddr_t addrhi = 0;
4715678Spl196000 	struct aac_sge *sge, *sge0;
4725678Spl196000 	int sg64;
4735678Spl196000 	int rval;
4745678Spl196000 
4755678Spl196000 	DBCALLED(softs, 2);
4765678Spl196000 
4775678Spl196000 	/* Read srb size */
4785678Spl196000 	if (ddi_copyin(&((struct aac_srb *)arg)->count, &usr_fib_size,
4795678Spl196000 	    sizeof (uint32_t), mode) != 0)
4805678Spl196000 		return (EFAULT);
4815678Spl196000 	if (usr_fib_size > (softs->aac_max_fib_size - \
4825678Spl196000 	    sizeof (struct aac_fib_header)))
4835678Spl196000 		return (EINVAL);
4845678Spl196000 
4855678Spl196000 	if ((acp = kmem_zalloc(sizeof (struct aac_cmd) + usr_fib_size + \
4865678Spl196000 	    sizeof (struct aac_fib_header), KM_NOSLEEP)) == NULL)
4875678Spl196000 		return (ENOMEM);
4885678Spl196000 
4895678Spl196000 	acp->fibp = (struct aac_fib *)(acp + 1);
4905678Spl196000 	fibp = acp->fibp;
4915678Spl196000 	srb = (struct aac_srb *)fibp->data;
4925678Spl196000 
4935678Spl196000 	/* Copy in srb */
4945678Spl196000 	if (ddi_copyin((void *)arg, srb, usr_fib_size, mode) != 0) {
4955678Spl196000 		rval = EFAULT;
4965678Spl196000 		goto finish;
4975678Spl196000 	}
4985678Spl196000 
4995678Spl196000 	srb_sgcount = srb->sg.SgCount; /* No endianness conversion needed */
5005678Spl196000 	if (srb_sgcount == 0)
5015678Spl196000 		goto send_fib;
5025678Spl196000 
5035678Spl196000 	/* Check FIB size */
5045678Spl196000 	if (usr_fib_size == (sizeof (struct aac_srb) + \
5055678Spl196000 	    srb_sgcount * sizeof (struct aac_sg_entry64) - \
5065678Spl196000 	    sizeof (struct aac_sg_entry))) {
5075678Spl196000 		sg64 = 1;
5085678Spl196000 	} else if (usr_fib_size == (sizeof (struct aac_srb) + \
5095678Spl196000 	    (srb_sgcount - 1) * sizeof (struct aac_sg_entry))) {
5105678Spl196000 		sg64 = 0;
5115678Spl196000 	} else {
5125678Spl196000 		rval = EINVAL;
5135678Spl196000 		goto finish;
5145678Spl196000 	}
5155678Spl196000 
5165678Spl196000 	/* Read user SG table */
5175678Spl196000 	if ((usgt = kmem_zalloc(sizeof (struct aac_umem_sge) * srb_sgcount,
5185678Spl196000 	    KM_NOSLEEP)) == NULL) {
5195678Spl196000 		rval = ENOMEM;
5205678Spl196000 		goto finish;
5215678Spl196000 	}
5225678Spl196000 	for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
5235678Spl196000 		if (sg64) {
5246799Sjd218194 			struct aac_sg_entry64 *sg64p =
5256799Sjd218194 			    (struct aac_sg_entry64 *)srb->sg.SgEntry;
5266096Sjd218194 
5276799Sjd218194 			usge->bcount = sg64p->SgByteCount;
5286096Sjd218194 			usge->addr = (caddr_t)
5296096Sjd218194 #ifndef _LP64
5306096Sjd218194 			    (uint32_t)
5316096Sjd218194 #endif
5326799Sjd218194 			    sg64p->SgAddress;
5335678Spl196000 		} else {
5346799Sjd218194 			struct aac_sg_entry *sgp = srb->sg.SgEntry;
5356799Sjd218194 
5366799Sjd218194 			usge->bcount = sgp->SgByteCount;
5375678Spl196000 			usge->addr = (caddr_t)
5385678Spl196000 #ifdef _LP64
5395678Spl196000 			    (uint64_t)
5405678Spl196000 #endif
5416799Sjd218194 			    sgp->SgAddress;
5425678Spl196000 		}
5435678Spl196000 		acp->bcount += usge->bcount;
5445678Spl196000 		if (usge->addr < addrlo)
5455678Spl196000 			addrlo = usge->addr;
5465678Spl196000 		if ((usge->addr + usge->bcount) > addrhi)
5475678Spl196000 			addrhi = usge->addr + usge->bcount;
5485678Spl196000 	}
5495678Spl196000 	if (acp->bcount > softs->buf_dma_attr.dma_attr_maxxfer) {
5505678Spl196000 		AACDB_PRINT(softs, CE_NOTE,
5515678Spl196000 		    "large srb xfer size received %d\n", acp->bcount);
5525678Spl196000 		rval = EINVAL;
5535678Spl196000 		goto finish;
5545678Spl196000 	}
5555678Spl196000 
5565678Spl196000 	/* Lock user buffers */
5575678Spl196000 	if (srb->flags & SRB_DataIn) {
5585678Spl196000 		umem_flags |= DDI_UMEMLOCK_READ;
5595678Spl196000 		direct |= B_READ;
5605678Spl196000 	}
5615678Spl196000 	if (srb->flags & SRB_DataOut) {
5625678Spl196000 		umem_flags |= DDI_UMEMLOCK_WRITE;
5635678Spl196000 		direct |= B_WRITE;
5645678Spl196000 	}
5655678Spl196000 	addrlo = (caddr_t)((uintptr_t)addrlo & (uintptr_t)PAGEMASK);
5665678Spl196000 	rval = ddi_umem_lock(addrlo, (((size_t)addrhi + PAGEOFFSET) & \
5675678Spl196000 	    PAGEMASK) - (size_t)addrlo, umem_flags, &cookie);
5685678Spl196000 	if (rval != 0) {
5695678Spl196000 		AACDB_PRINT(softs, CE_NOTE, "ddi_umem_lock failed: %d",
5705678Spl196000 		    rval);
5715678Spl196000 		goto finish;
5725678Spl196000 	}
5735678Spl196000 	locked = 1;
5745678Spl196000 
5755678Spl196000 	/* Allocate DMA for user buffers */
5765678Spl196000 	for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
5775678Spl196000 		struct buf *bp;
5785678Spl196000 
5797100Spl196000 		bp = ddi_umem_iosetup(cookie, (uintptr_t)usge->addr - \
5807100Spl196000 		    (uintptr_t)addrlo, usge->bcount, direct, dev, 0, NULL,
5817567SXin.Chen@Sun.COM 		    DDI_UMEM_NOSLEEP);
5825678Spl196000 		if (bp == NULL) {
5835678Spl196000 			AACDB_PRINT(softs, CE_NOTE, "ddi_umem_iosetup failed");
5847567SXin.Chen@Sun.COM 			rval = ENOMEM;
5855678Spl196000 			goto finish;
5865678Spl196000 		}
5875678Spl196000 		if (aac_cmd_dma_alloc(softs, &usge->acp, bp, 0, NULL_FUNC,
5885678Spl196000 		    0) != AACOK) {
5895678Spl196000 			rval = EFAULT;
5905678Spl196000 			goto finish;
5915678Spl196000 		}
5925678Spl196000 		acp->left_cookien += usge->acp.left_cookien;
5935678Spl196000 		if (acp->left_cookien > softs->aac_sg_tablesize) {
5945678Spl196000 			AACDB_PRINT(softs, CE_NOTE, "large cookiec received %d",
5955678Spl196000 			    acp->left_cookien);
5965678Spl196000 			rval = EINVAL;
5975678Spl196000 			goto finish;
5985678Spl196000 		}
5995678Spl196000 	}
6005678Spl196000 
6015678Spl196000 	/* Construct aac cmd SG table */
6025678Spl196000 	if ((sge = kmem_zalloc(sizeof (struct aac_sge) * acp->left_cookien,
6035678Spl196000 	    KM_NOSLEEP)) == NULL) {
6045678Spl196000 		rval = ENOMEM;
6055678Spl196000 		goto finish;
6065678Spl196000 	}
6075678Spl196000 	acp->sgt = sge;
6085678Spl196000 	for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
6095678Spl196000 		for (sge0 = usge->acp.sgt;
6105678Spl196000 		    sge0 < &usge->acp.sgt[usge->acp.left_cookien];
6115678Spl196000 		    sge0++, sge++)
6125678Spl196000 			*sge = *sge0;
6135678Spl196000 	}
6145678Spl196000 
6155678Spl196000 send_fib:
6165678Spl196000 	acp->cmdlen = srb->cdb_size;
6175678Spl196000 	acp->timeout = srb->timeout;
6185678Spl196000 
6195678Spl196000 	/* Send FIB command */
6205678Spl196000 	acp->aac_cmd_fib = softs->aac_cmd_fib_scsi;
6217567SXin.Chen@Sun.COM #ifdef DEBUG
6227567SXin.Chen@Sun.COM 	acp->fib_flags = AACDB_FLAGS_FIB_SRB;
6237567SXin.Chen@Sun.COM #endif
6245678Spl196000 	if ((rval = aac_send_fib(softs, acp)) != 0)
6255678Spl196000 		goto finish;
6265678Spl196000 
6275678Spl196000 	/* Status struct */
6285678Spl196000 	if (ddi_copyout((struct aac_srb_reply *)fibp->data,
6295678Spl196000 	    ((uint8_t *)arg + usr_fib_size),
6305678Spl196000 	    sizeof (struct aac_srb_reply), mode) != 0) {
6315678Spl196000 		rval = EFAULT;
6325678Spl196000 		goto finish;
6335678Spl196000 	}
6345678Spl196000 
6355678Spl196000 	rval = 0;
6365678Spl196000 finish:
6375678Spl196000 	if (acp->sgt)
6385678Spl196000 		kmem_free(acp->sgt, sizeof (struct aac_sge) * \
6395678Spl196000 		    acp->left_cookien);
6405678Spl196000 	if (usgt) {
6415678Spl196000 		for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
6425678Spl196000 			if (usge->acp.sgt)
6435678Spl196000 				kmem_free(usge->acp.sgt,
6445678Spl196000 				    sizeof (struct aac_sge) * \
6455678Spl196000 				    usge->acp.left_cookien);
6465678Spl196000 			aac_free_dmamap(&usge->acp);
6475678Spl196000 			if (usge->acp.bp)
6485678Spl196000 				freerbuf(usge->acp.bp);
6495678Spl196000 		}
6505678Spl196000 		kmem_free(usgt, sizeof (struct aac_umem_sge) * srb_sgcount);
6515678Spl196000 	}
6525678Spl196000 	if (locked)
6535678Spl196000 		ddi_umem_unlock(cookie);
6545678Spl196000 	kmem_free(acp, sizeof (struct aac_cmd) + usr_fib_size + \
6555678Spl196000 	    sizeof (struct aac_fib_header));
6565678Spl196000 	return (rval);
6575678Spl196000 }
6585678Spl196000 
6595678Spl196000 /*ARGSUSED*/
6605678Spl196000 static int
6615678Spl196000 aac_get_pci_info(struct aac_softstate *softs, intptr_t arg, int mode)
6625678Spl196000 {
6635678Spl196000 	union aac_pci_info_align un;
6645678Spl196000 	struct aac_pci_info *resp = &un.d;
665*11348SZhongyan.Gu@Sun.COM 	pci_regspec_t *pci_rp;
666*11348SZhongyan.Gu@Sun.COM 	uint_t num;
6675678Spl196000 
6685678Spl196000 	DBCALLED(softs, 2);
6695678Spl196000 
670*11348SZhongyan.Gu@Sun.COM 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, softs->devinfo_p,
671*11348SZhongyan.Gu@Sun.COM 	    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &num) !=
672*11348SZhongyan.Gu@Sun.COM 	    DDI_PROP_SUCCESS)
673*11348SZhongyan.Gu@Sun.COM 		return (EINVAL);
674*11348SZhongyan.Gu@Sun.COM 	if (num < (sizeof (pci_regspec_t) / sizeof (int))) {
675*11348SZhongyan.Gu@Sun.COM 		ddi_prop_free(pci_rp);
676*11348SZhongyan.Gu@Sun.COM 		return (EINVAL);
677*11348SZhongyan.Gu@Sun.COM 	}
678*11348SZhongyan.Gu@Sun.COM 
679*11348SZhongyan.Gu@Sun.COM 	resp->bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
680*11348SZhongyan.Gu@Sun.COM 	resp->slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
681*11348SZhongyan.Gu@Sun.COM 	ddi_prop_free(pci_rp);
6825678Spl196000 
6835678Spl196000 	if (ddi_copyout(resp, (void *)arg,
6845678Spl196000 	    sizeof (struct aac_pci_info), mode) != 0)
6855678Spl196000 		return (EFAULT);
6865678Spl196000 	return (0);
6875678Spl196000 }
6885678Spl196000 
6895678Spl196000 static int
6905678Spl196000 aac_query_disk(struct aac_softstate *softs, intptr_t arg, int mode)
6915678Spl196000 {
6925678Spl196000 	union aac_query_disk_align un;
6935678Spl196000 	struct aac_query_disk *qdisk = &un.d;
6945678Spl196000 	struct aac_container *dvp;
6955678Spl196000 
6965678Spl196000 	DBCALLED(softs, 2);
6975678Spl196000 
6985678Spl196000 	if (ddi_copyin((void *)arg, qdisk, sizeof (*qdisk), mode) != 0)
6995678Spl196000 		return (EFAULT);
7005678Spl196000 
7015678Spl196000 	if (qdisk->container_no == -1) {
7025678Spl196000 		qdisk->container_no = qdisk->target * 16 + qdisk->lun;
7035678Spl196000 	} else if (qdisk->bus == -1 && qdisk->target == -1 &&
7045678Spl196000 	    qdisk->lun == -1) {
7055678Spl196000 		if (qdisk->container_no >= AAC_MAX_CONTAINERS)
7065678Spl196000 			return (EINVAL);
7075678Spl196000 		qdisk->bus = 0;
7085678Spl196000 		qdisk->target = (qdisk->container_no & 0xf);
7095678Spl196000 		qdisk->lun = (qdisk->container_no >> 4);
7105678Spl196000 	} else {
7115678Spl196000 		return (EINVAL);
7125678Spl196000 	}
7135678Spl196000 
7145678Spl196000 	mutex_enter(&softs->io_lock);
7155678Spl196000 	dvp = &softs->containers[qdisk->container_no];
7167567SXin.Chen@Sun.COM 	qdisk->valid = AAC_DEV_IS_VALID(&dvp->dev);
7175678Spl196000 	qdisk->locked = dvp->locked;
7185678Spl196000 	qdisk->deleted = dvp->deleted;
7195678Spl196000 	mutex_exit(&softs->io_lock);
7205678Spl196000 
7215678Spl196000 	if (ddi_copyout(qdisk, (void *)arg, sizeof (*qdisk), mode) != 0)
7225678Spl196000 		return (EFAULT);
7235678Spl196000 	return (0);
7245678Spl196000 }
7255678Spl196000 
7265678Spl196000 static int
7275678Spl196000 aac_delete_disk(struct aac_softstate *softs, intptr_t arg, int mode)
7285678Spl196000 {
7295678Spl196000 	union aac_delete_disk_align un;
7305678Spl196000 	struct aac_delete_disk *ddisk = &un.d;
7315678Spl196000 	struct aac_container *dvp;
7325678Spl196000 	int rval = 0;
7335678Spl196000 
7345678Spl196000 	DBCALLED(softs, 2);
7355678Spl196000 
7365678Spl196000 	if (ddi_copyin((void *)arg, ddisk, sizeof (*ddisk), mode) != 0)
7375678Spl196000 		return (EFAULT);
7385678Spl196000 
7395678Spl196000 	if (ddisk->container_no >= AAC_MAX_CONTAINERS)
7405678Spl196000 		return (EINVAL);
7415678Spl196000 
7425678Spl196000 	mutex_enter(&softs->io_lock);
7435678Spl196000 	dvp = &softs->containers[ddisk->container_no];
7445678Spl196000 	/*
7455678Spl196000 	 * We don't trust the userland to tell us when to delete
7465678Spl196000 	 * a container, rather we rely on an AIF coming from the
7475678Spl196000 	 * controller.
7485678Spl196000 	 */
7497567SXin.Chen@Sun.COM 	if (AAC_DEV_IS_VALID(&dvp->dev)) {
7505678Spl196000 		if (dvp->locked)
7515678Spl196000 			rval = EBUSY;
7525678Spl196000 	}
7535678Spl196000 	mutex_exit(&softs->io_lock);
7545678Spl196000 
7555678Spl196000 	return (rval);
7565678Spl196000 }
7575678Spl196000 
7585678Spl196000 /*
7595678Spl196000  * The following function comes from Adaptec to support creation of arrays
7605678Spl196000  * bigger than 2TB.
7615678Spl196000  */
7625678Spl196000 static int
7635678Spl196000 aac_supported_features(struct aac_softstate *softs, intptr_t arg, int mode)
7645678Spl196000 {
7655678Spl196000 	union aac_features_align un;
7665678Spl196000 	struct aac_features *f = &un.d;
7675678Spl196000 
7685678Spl196000 	DBCALLED(softs, 2);
7695678Spl196000 
7705678Spl196000 	if (ddi_copyin((void *)arg, f, sizeof (*f), mode) != 0)
7715678Spl196000 		return (EFAULT);
7725678Spl196000 
7735678Spl196000 	/*
7745678Spl196000 	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
7755678Spl196000 	 * ALL zero in the featuresState, the driver will return the current
7765678Spl196000 	 * state of all the supported features, the data field will not be
7775678Spl196000 	 * valid.
7785678Spl196000 	 * When the management driver receives FSACTL_GET_FEATURES ioctl with
7795678Spl196000 	 * a specific bit set in the featuresState, the driver will return the
7805678Spl196000 	 * current state of this specific feature and whatever data that are
7815678Spl196000 	 * associated with the feature in the data field or perform whatever
7825678Spl196000 	 * action needed indicates in the data field.
7835678Spl196000 	 */
7845678Spl196000 	if (f->feat.fValue == 0) {
7855678Spl196000 		f->feat.fBits.largeLBA =
7865678Spl196000 		    (softs->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
7875678Spl196000 		/* TODO: In the future, add other features state here as well */
7885678Spl196000 	} else {
7895678Spl196000 		if (f->feat.fBits.largeLBA)
7905678Spl196000 			f->feat.fBits.largeLBA =
7915678Spl196000 			    (softs->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
7925678Spl196000 		/* TODO: Add other features state and data in the future */
7935678Spl196000 	}
7945678Spl196000 
7955678Spl196000 	if (ddi_copyout(f, (void *)arg, sizeof (*f), mode) != 0)
7965678Spl196000 		return (EFAULT);
7975678Spl196000 	return (0);
7985678Spl196000 }
799