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