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 
  51 struct aac_umem_sge {
  52         uint32_t bcount;
  53         caddr_t addr;
  54         struct aac_cmd acp;
  55 };
  56 
  57 /*
  58  * External functions
  59  */
  60 extern int aac_sync_mbcommand(struct aac_softstate *, uint32_t, uint32_t,
  61     uint32_t, uint32_t, uint32_t, uint32_t *);
  62 extern int aac_cmd_dma_alloc(struct aac_softstate *, struct aac_cmd *,
  63     struct buf *, int, int (*)(), caddr_t);
  64 extern void aac_free_dmamap(struct aac_cmd *);
  65 extern int aac_do_io(struct aac_softstate *, struct aac_cmd *);
  66 extern void aac_cmd_fib_copy(struct aac_softstate *, struct aac_cmd *);
  67 extern void aac_ioctl_complete(struct aac_softstate *, struct aac_cmd *);
  68 extern int aac_return_aif_wait(struct aac_softstate *, struct aac_fib_context *,
  69     struct aac_fib **);
  70 extern int aac_return_aif(struct aac_softstate *, struct aac_fib_context *,
  71     struct aac_fib **);
  72 
  73 extern ddi_device_acc_attr_t aac_acc_attr;
  74 extern int aac_check_dma_handle(ddi_dma_handle_t);
  75 
  76 /*
  77  * IOCTL command handling functions
  78  */
  79 static int aac_check_revision(struct aac_softstate *, intptr_t, int);
  80 static int aac_ioctl_send_fib(struct aac_softstate *, intptr_t, int);
  81 static int aac_open_getadapter_fib(struct aac_softstate *, intptr_t, int);
  82 static int aac_next_getadapter_fib(struct aac_softstate *, intptr_t, int);
  83 static int aac_close_getadapter_fib(struct aac_softstate *, intptr_t);
  84 static int aac_send_raw_srb(struct aac_softstate *, dev_t, intptr_t, int);
  85 static int aac_get_pci_info(struct aac_softstate *, intptr_t, int);
  86 static int aac_query_disk(struct aac_softstate *, intptr_t, int);
  87 static int aac_delete_disk(struct aac_softstate *, intptr_t, int);
  88 static 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 
  96 int
  97 aac_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");
 112 send_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*/
 158 static int
 159 aac_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 
 187 static int
 188 aac_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 
 216 static int
 217 aac_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;
 303 finish:
 304         kmem_free(acp, hbalen);
 305         return (rval);
 306 }
 307 
 308 static int
 309 aac_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 
 356 static int
 357 aac_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 
 386 finish:
 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 
 400 static int
 401 aac_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  */
 433 static int
 434 aac_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 
 593 send_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;
 614 finish:
 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*/
 638 static int
 639 aac_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 
 667 static int
 668 aac_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 
 704 static int
 705 aac_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  */
 740 static int
 741 aac_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 }