/* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 2023 Oxide Computer Company * Copyright (c) 2018, Joyent, Inc. * Copyright 2005-08 Adaptec, Inc. * Copyright (c) 2005-08 Adaptec Inc., Achim Leubner * Copyright (c) 2000 Michael Smith * Copyright (c) 2001 Scott Long * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "aac_regs.h" #include "aac.h" /* * FMA header files */ #include #include #include #include /* * For minor nodes created by the SCSA framework, minor numbers are * formed by left-shifting instance by INST_MINOR_SHIFT and OR in a * number less than 64. * * To support cfgadm, need to confirm the SCSA framework by creating * devctl/scsi and driver specific minor nodes under SCSA format, * and calling scsi_hba_xxx() functions aacordingly. */ #define AAC_MINOR 32 #define INST2AAC(x) (((x) << INST_MINOR_SHIFT) | AAC_MINOR) #define AAC_SCSA_MINOR(x) ((x) & TRAN_MINOR_MASK) #define AAC_IS_SCSA_NODE(x) ((x) == DEVCTL_MINOR || (x) == SCSI_MINOR) #define SD2TRAN(sd) ((sd)->sd_address.a_hba_tran) #define AAC_TRAN2SOFTS(tran) ((struct aac_softstate *)(tran)->tran_hba_private) #define AAC_DIP2TRAN(dip) ((scsi_hba_tran_t *)ddi_get_driver_private(dip)) #define AAC_DIP2SOFTS(dip) (AAC_TRAN2SOFTS(AAC_DIP2TRAN(dip))) #define SD2AAC(sd) (AAC_TRAN2SOFTS(SD2TRAN(sd))) #define AAC_PD(t) ((t) - AAC_MAX_LD) #define AAC_DEV(softs, t) (((t) < AAC_MAX_LD) ? \ &(softs)->containers[(t)].dev : \ ((t) < AAC_MAX_DEV(softs)) ? \ &(softs)->nondasds[AAC_PD(t)].dev : NULL) #define AAC_DEVCFG_BEGIN(softs, tgt) \ aac_devcfg((softs), (tgt), 1) #define AAC_DEVCFG_END(softs, tgt) \ aac_devcfg((softs), (tgt), 0) #define PKT2AC(pkt) ((struct aac_cmd *)(pkt)->pkt_ha_private) #define AAC_BUSYWAIT(cond, timeout /* in millisecond */) { \ if (!(cond)) { \ int count = (timeout) * 10; \ while (count) { \ drv_usecwait(100); \ if (cond) \ break; \ count--; \ } \ (timeout) = (count + 9) / 10; \ } \ } #define AAC_SENSE_DATA_DESCR_LEN \ (sizeof (struct scsi_descr_sense_hdr) + \ sizeof (struct scsi_information_sense_descr)) #define AAC_ARQ64_LENGTH \ (sizeof (struct scsi_arq_status) + \ AAC_SENSE_DATA_DESCR_LEN - SENSE_LENGTH) /* NOTE: GETG4ADDRTL(cdbp) is int32_t */ #define AAC_GETGXADDR(cmdlen, cdbp) \ ((cmdlen == 6) ? GETG0ADDR(cdbp) : \ (cmdlen == 10) ? (uint32_t)GETG1ADDR(cdbp) : \ ((uint64_t)GETG4ADDR(cdbp) << 32) | (uint32_t)GETG4ADDRTL(cdbp)) #define AAC_CDB_INQUIRY_CMDDT 0x02 #define AAC_CDB_INQUIRY_EVPD 0x01 #define AAC_VPD_PAGE_CODE 1 #define AAC_VPD_PAGE_LENGTH 3 #define AAC_VPD_PAGE_DATA 4 #define AAC_VPD_ID_CODESET 0 #define AAC_VPD_ID_TYPE 1 #define AAC_VPD_ID_LENGTH 3 #define AAC_VPD_ID_DATA 4 #define AAC_SCSI_RPTLUNS_HEAD_SIZE 0x08 #define AAC_SCSI_RPTLUNS_ADDR_SIZE 0x08 #define AAC_SCSI_RPTLUNS_ADDR_MASK 0xC0 /* 00b - peripheral device addressing method */ #define AAC_SCSI_RPTLUNS_ADDR_PERIPHERAL 0x00 /* 01b - flat space addressing method */ #define AAC_SCSI_RPTLUNS_ADDR_FLAT_SPACE 0x40 /* 10b - logical unit addressing method */ #define AAC_SCSI_RPTLUNS_ADDR_LOGICAL_UNIT 0x80 /* Return the size of FIB with data part type data_type */ #define AAC_FIB_SIZEOF(data_type) \ (sizeof (struct aac_fib_header) + sizeof (data_type)) /* Return the container size defined in mir */ #define AAC_MIR_SIZE(softs, acc, mir) \ (((softs)->flags & AAC_FLAGS_LBA_64BIT) ? \ (uint64_t)ddi_get32((acc), &(mir)->MntObj.Capacity) + \ ((uint64_t)ddi_get32((acc), &(mir)->MntObj.CapacityHigh) << 32) : \ (uint64_t)ddi_get32((acc), &(mir)->MntObj.Capacity)) /* The last entry of aac_cards[] is for unknown cards */ #define AAC_UNKNOWN_CARD \ (sizeof (aac_cards) / sizeof (struct aac_card_type) - 1) #define CARD_IS_UNKNOWN(i) (i == AAC_UNKNOWN_CARD) #define BUF_IS_READ(bp) ((bp)->b_flags & B_READ) #define AAC_IS_Q_EMPTY(q) ((q)->q_head == NULL) #define AAC_CMDQ(acp) (!((acp)->flags & AAC_CMD_SYNC)) #define PCI_MEM_GET32(softs, off) \ ddi_get32((softs)->pci_mem_handle, \ (void *)((softs)->pci_mem_base_vaddr + (off))) #define PCI_MEM_PUT32(softs, off, val) \ ddi_put32((softs)->pci_mem_handle, \ (void *)((softs)->pci_mem_base_vaddr + (off)), \ (uint32_t)(val)) #define PCI_MEM_GET16(softs, off) \ ddi_get16((softs)->pci_mem_handle, \ (void *)((softs)->pci_mem_base_vaddr + (off))) #define PCI_MEM_PUT16(softs, off, val) \ ddi_put16((softs)->pci_mem_handle, \ (void *)((softs)->pci_mem_base_vaddr + (off)), (uint16_t)(val)) /* Write host data at valp to device mem[off] repeatedly count times */ #define PCI_MEM_REP_PUT8(softs, off, valp, count) \ ddi_rep_put8((softs)->pci_mem_handle, (uint8_t *)(valp), \ (uint8_t *)((softs)->pci_mem_base_vaddr + (off)), \ count, DDI_DEV_AUTOINCR) /* Read device data at mem[off] to host addr valp repeatedly count times */ #define PCI_MEM_REP_GET8(softs, off, valp, count) \ ddi_rep_get8((softs)->pci_mem_handle, (uint8_t *)(valp), \ (uint8_t *)((softs)->pci_mem_base_vaddr + (off)), \ count, DDI_DEV_AUTOINCR) #define AAC_GET_FIELD8(acc, d, s, field) \ (d)->field = ddi_get8(acc, (uint8_t *)&(s)->field) #define AAC_GET_FIELD32(acc, d, s, field) \ (d)->field = ddi_get32(acc, (uint32_t *)&(s)->field) #define AAC_GET_FIELD64(acc, d, s, field) \ (d)->field = ddi_get64(acc, (uint64_t *)&(s)->field) #define AAC_REP_GET_FIELD8(acc, d, s, field, r) \ ddi_rep_get8((acc), (uint8_t *)&(d)->field, \ (uint8_t *)&(s)->field, (r), DDI_DEV_AUTOINCR) #define AAC_REP_GET_FIELD32(acc, d, s, field, r) \ ddi_rep_get32((acc), (uint32_t *)&(d)->field, \ (uint32_t *)&(s)->field, (r), DDI_DEV_AUTOINCR) #define AAC_ENABLE_INTR(softs) { \ if (softs->flags & AAC_FLAGS_NEW_COMM) \ PCI_MEM_PUT32(softs, AAC_OIMR, ~AAC_DB_INTR_NEW); \ else \ PCI_MEM_PUT32(softs, AAC_OIMR, ~AAC_DB_INTR_BITS); \ softs->state |= AAC_STATE_INTR; \ } #define AAC_DISABLE_INTR(softs) { \ PCI_MEM_PUT32(softs, AAC_OIMR, ~0); \ softs->state &= ~AAC_STATE_INTR; \ } #define AAC_STATUS_CLR(softs, mask) PCI_MEM_PUT32(softs, AAC_ODBR, mask) #define AAC_STATUS_GET(softs) PCI_MEM_GET32(softs, AAC_ODBR) #define AAC_NOTIFY(softs, val) PCI_MEM_PUT32(softs, AAC_IDBR, val) #define AAC_OUTB_GET(softs) PCI_MEM_GET32(softs, AAC_OQUE) #define AAC_OUTB_SET(softs, val) PCI_MEM_PUT32(softs, AAC_OQUE, val) #define AAC_FWSTATUS_GET(softs) \ ((softs)->aac_if.aif_get_fwstatus(softs)) #define AAC_MAILBOX_GET(softs, mb) \ ((softs)->aac_if.aif_get_mailbox((softs), (mb))) #define AAC_MAILBOX_SET(softs, cmd, arg0, arg1, arg2, arg3) \ ((softs)->aac_if.aif_set_mailbox((softs), (cmd), \ (arg0), (arg1), (arg2), (arg3))) #define AAC_MGT_SLOT_NUM 2 #define AAC_THROTTLE_DRAIN -1 #define AAC_QUIESCE_TICK 1 /* 1 second */ #define AAC_QUIESCE_TIMEOUT 180 /* 180 seconds */ #define AAC_DEFAULT_TICK 10 /* 10 seconds */ #define AAC_SYNC_TICK (30*60) /* 30 minutes */ /* Poll time for aac_do_poll_io() */ #define AAC_POLL_TIME 60 /* 60 seconds */ /* IOP reset */ #define AAC_IOP_RESET_SUCCEED 0 /* IOP reset succeed */ #define AAC_IOP_RESET_FAILED -1 /* IOP reset failed */ #define AAC_IOP_RESET_ABNORMAL -2 /* Reset operation abnormal */ /* * Hardware access functions */ static int aac_rx_get_fwstatus(struct aac_softstate *); static int aac_rx_get_mailbox(struct aac_softstate *, int); static void aac_rx_set_mailbox(struct aac_softstate *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); static int aac_rkt_get_fwstatus(struct aac_softstate *); static int aac_rkt_get_mailbox(struct aac_softstate *, int); static void aac_rkt_set_mailbox(struct aac_softstate *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); /* * SCSA function prototypes */ static int aac_attach(dev_info_t *, ddi_attach_cmd_t); static int aac_detach(dev_info_t *, ddi_detach_cmd_t); static int aac_reset(dev_info_t *, ddi_reset_cmd_t); static int aac_quiesce(dev_info_t *); static int aac_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); /* * Interrupt handler functions */ static int aac_query_intrs(struct aac_softstate *, int); static int aac_add_intrs(struct aac_softstate *); static void aac_remove_intrs(struct aac_softstate *); static int aac_enable_intrs(struct aac_softstate *); static int aac_disable_intrs(struct aac_softstate *); static uint_t aac_intr_old(caddr_t, caddr_t); static uint_t aac_intr_new(caddr_t, caddr_t); static uint_t aac_softintr(caddr_t); /* * Internal functions in attach */ static int aac_check_card_type(struct aac_softstate *); static int aac_check_firmware(struct aac_softstate *); static int aac_common_attach(struct aac_softstate *); static void aac_common_detach(struct aac_softstate *); static int aac_probe_containers(struct aac_softstate *); static int aac_alloc_comm_space(struct aac_softstate *); static int aac_setup_comm_space(struct aac_softstate *); static void aac_free_comm_space(struct aac_softstate *); static int aac_hba_setup(struct aac_softstate *); /* * Sync FIB operation functions */ int aac_sync_mbcommand(struct aac_softstate *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t *); static int aac_sync_fib(struct aac_softstate *, uint16_t, uint16_t); /* * Command queue operation functions */ static void aac_cmd_initq(struct aac_cmd_queue *); static void aac_cmd_enqueue(struct aac_cmd_queue *, struct aac_cmd *); static struct aac_cmd *aac_cmd_dequeue(struct aac_cmd_queue *); static void aac_cmd_delete(struct aac_cmd_queue *, struct aac_cmd *); /* * FIB queue operation functions */ static int aac_fib_enqueue(struct aac_softstate *, int, uint32_t, uint32_t); static int aac_fib_dequeue(struct aac_softstate *, int, int *); /* * Slot operation functions */ static int aac_create_slots(struct aac_softstate *); static void aac_destroy_slots(struct aac_softstate *); static void aac_alloc_fibs(struct aac_softstate *); static void aac_destroy_fibs(struct aac_softstate *); static struct aac_slot *aac_get_slot(struct aac_softstate *); static void aac_release_slot(struct aac_softstate *, struct aac_slot *); static int aac_alloc_fib(struct aac_softstate *, struct aac_slot *); static void aac_free_fib(struct aac_slot *); /* * Internal functions */ static void aac_cmd_fib_header(struct aac_softstate *, struct aac_cmd *, uint16_t); static void aac_cmd_fib_rawio(struct aac_softstate *, struct aac_cmd *); static void aac_cmd_fib_brw64(struct aac_softstate *, struct aac_cmd *); static void aac_cmd_fib_brw(struct aac_softstate *, struct aac_cmd *); static void aac_cmd_fib_sync(struct aac_softstate *, struct aac_cmd *); static void aac_cmd_fib_scsi32(struct aac_softstate *, struct aac_cmd *); static void aac_cmd_fib_scsi64(struct aac_softstate *, struct aac_cmd *); static void aac_cmd_fib_startstop(struct aac_softstate *, struct aac_cmd *); static void aac_start_waiting_io(struct aac_softstate *); static void aac_drain_comp_q(struct aac_softstate *); int aac_do_io(struct aac_softstate *, struct aac_cmd *); static int aac_sync_fib_slot_bind(struct aac_softstate *, struct aac_cmd *); static void aac_sync_fib_slot_release(struct aac_softstate *, struct aac_cmd *); static void aac_start_io(struct aac_softstate *, struct aac_cmd *); static int aac_do_poll_io(struct aac_softstate *, struct aac_cmd *); static int aac_do_sync_io(struct aac_softstate *, struct aac_cmd *); static int aac_send_command(struct aac_softstate *, struct aac_slot *); static void aac_cmd_timeout(struct aac_softstate *, struct aac_cmd *); static int aac_dma_sync_ac(struct aac_cmd *); static int aac_shutdown(struct aac_softstate *); static int aac_reset_adapter(struct aac_softstate *); static int aac_do_quiesce(struct aac_softstate *softs); static int aac_do_unquiesce(struct aac_softstate *softs); static void aac_unhold_bus(struct aac_softstate *, int); static void aac_set_throttle(struct aac_softstate *, struct aac_device *, int, int); /* * Adapter Initiated FIB handling function */ static void aac_save_aif(struct aac_softstate *, ddi_acc_handle_t, struct aac_fib *, int); static int aac_handle_aif(struct aac_softstate *, struct aac_aif_command *); /* * Event handling related functions */ static void aac_timer(void *); static void aac_event_thread(struct aac_softstate *); static void aac_event_disp(struct aac_softstate *, int); /* * IOCTL interface related functions */ static int aac_open(dev_t *, int, int, cred_t *); static int aac_close(dev_t, int, int, cred_t *); static int aac_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); extern int aac_do_ioctl(struct aac_softstate *, dev_t, int, intptr_t, int); /* * FMA Prototypes */ static void aac_fm_init(struct aac_softstate *); static void aac_fm_fini(struct aac_softstate *); static int aac_fm_error_cb(dev_info_t *, ddi_fm_error_t *, const void *); int aac_check_acc_handle(ddi_acc_handle_t); int aac_check_dma_handle(ddi_dma_handle_t); void aac_fm_ereport(struct aac_softstate *, char *); /* * Auto enumeration functions */ static dev_info_t *aac_find_child(struct aac_softstate *, uint16_t, uint8_t); static int aac_tran_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *, dev_info_t **); static int aac_handle_dr(struct aac_softstate *, int, int, int); extern pri_t minclsyspri; #ifdef DEBUG /* * UART debug output support */ #define AAC_PRINT_BUFFER_SIZE 512 #define AAC_PRINT_TIMEOUT 250 /* 1/4 sec. = 250 msec. */ #define AAC_FW_DBG_STRLEN_OFFSET 0x00 #define AAC_FW_DBG_FLAGS_OFFSET 0x04 #define AAC_FW_DBG_BLED_OFFSET 0x08 static int aac_get_fw_debug_buffer(struct aac_softstate *); static void aac_print_scmd(struct aac_softstate *, struct aac_cmd *); static void aac_print_aif(struct aac_softstate *, struct aac_aif_command *); static char aac_prt_buf[AAC_PRINT_BUFFER_SIZE]; static char aac_fmt[] = " %s"; static char aac_fmt_header[] = " %s.%d: %s"; static kmutex_t aac_prt_mutex; /* * Debug flags to be put into the softstate flags field * when initialized */ uint32_t aac_debug_flags = /* AACDB_FLAGS_KERNEL_PRINT | */ /* AACDB_FLAGS_FW_PRINT | */ /* AACDB_FLAGS_MISC | */ /* AACDB_FLAGS_FUNC1 | */ /* AACDB_FLAGS_FUNC2 | */ /* AACDB_FLAGS_SCMD | */ /* AACDB_FLAGS_AIF | */ /* AACDB_FLAGS_FIB | */ /* AACDB_FLAGS_IOCTL | */ 0; uint32_t aac_debug_fib_flags = /* AACDB_FLAGS_FIB_RW | */ /* AACDB_FLAGS_FIB_IOCTL | */ /* AACDB_FLAGS_FIB_SRB | */ /* AACDB_FLAGS_FIB_SYNC | */ /* AACDB_FLAGS_FIB_HEADER | */ /* AACDB_FLAGS_FIB_TIMEOUT | */ 0; #endif /* DEBUG */ static struct cb_ops aac_cb_ops = { aac_open, /* open */ aac_close, /* close */ nodev, /* strategy */ nodev, /* print */ nodev, /* dump */ nodev, /* read */ nodev, /* write */ aac_ioctl, /* ioctl */ nodev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* poll */ ddi_prop_op, /* cb_prop_op */ NULL, /* streamtab */ D_64BIT | D_NEW | D_MP | D_HOTPLUG, /* cb_flag */ CB_REV, /* cb_rev */ nodev, /* async I/O read entry point */ nodev /* async I/O write entry point */ }; static struct dev_ops aac_dev_ops = { DEVO_REV, 0, aac_getinfo, nulldev, nulldev, aac_attach, aac_detach, aac_reset, &aac_cb_ops, NULL, NULL, aac_quiesce, }; static struct modldrv aac_modldrv = { &mod_driverops, "AAC Driver " AAC_DRIVER_VERSION, &aac_dev_ops, }; static struct modlinkage aac_modlinkage = { MODREV_1, &aac_modldrv, NULL }; static struct aac_softstate *aac_softstatep; /* * Supported card list * ordered in vendor id, subvendor id, subdevice id, and device id */ static struct aac_card_type aac_cards[] = { {0x1028, 0x1, 0x1028, 0x1, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG, AAC_TYPE_SCSI, "Dell", "PERC 3/Di"}, {0x1028, 0x2, 0x1028, 0x2, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG, AAC_TYPE_SCSI, "Dell", "PERC 3/Di"}, {0x1028, 0x3, 0x1028, 0x3, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG, AAC_TYPE_SCSI, "Dell", "PERC 3/Si"}, {0x1028, 0x8, 0x1028, 0xcf, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG, AAC_TYPE_SCSI, "Dell", "PERC 3/Di"}, {0x1028, 0x4, 0x1028, 0xd0, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG, AAC_TYPE_SCSI, "Dell", "PERC 3/Si"}, {0x1028, 0x2, 0x1028, 0xd1, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG, AAC_TYPE_SCSI, "Dell", "PERC 3/Di"}, {0x1028, 0x2, 0x1028, 0xd9, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG, AAC_TYPE_SCSI, "Dell", "PERC 3/Di"}, {0x1028, 0xa, 0x1028, 0x106, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG, AAC_TYPE_SCSI, "Dell", "PERC 3/Di"}, {0x1028, 0xa, 0x1028, 0x11b, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG, AAC_TYPE_SCSI, "Dell", "PERC 3/Di"}, {0x1028, 0xa, 0x1028, 0x121, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG, AAC_TYPE_SCSI, "Dell", "PERC 3/Di"}, {0x9005, 0x285, 0x1028, 0x287, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG | AAC_FLAGS_256FIBS, AAC_TYPE_SCSI, "Dell", "PERC 320/DC"}, {0x9005, 0x285, 0x1028, 0x291, AAC_HWIF_I960RX, AAC_FLAGS_17SG, AAC_TYPE_SATA, "Dell", "CERC SR2"}, {0x9005, 0x285, 0x1014, 0x2f2, AAC_HWIF_I960RX, 0, AAC_TYPE_SCSI, "IBM", "ServeRAID 8i"}, {0x9005, 0x285, 0x1014, 0x34d, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "IBM", "ServeRAID 8s"}, {0x9005, 0x286, 0x1014, 0x9580, AAC_HWIF_RKT, 0, AAC_TYPE_SAS, "IBM", "ServeRAID 8k"}, {0x9005, 0x285, 0x103c, 0x3227, AAC_HWIF_I960RX, AAC_FLAGS_17SG, AAC_TYPE_SATA, "Adaptec", "2610SA"}, {0x9005, 0x285, 0xe11, 0x295, AAC_HWIF_I960RX, AAC_FLAGS_17SG, AAC_TYPE_SATA, "Adaptec", "2610SA"}, {0x9005, 0x285, 0x9005, 0x285, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG | AAC_FLAGS_256FIBS, AAC_TYPE_SCSI, "Adaptec", "2200S"}, {0x9005, 0x285, 0x9005, 0x286, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG | AAC_FLAGS_256FIBS, AAC_TYPE_SCSI, "Adaptec", "2120S"}, {0x9005, 0x285, 0x9005, 0x287, AAC_HWIF_I960RX, AAC_FLAGS_NO4GB | AAC_FLAGS_34SG | AAC_FLAGS_256FIBS, AAC_TYPE_SCSI, "Adaptec", "2200S"}, {0x9005, 0x285, 0x9005, 0x288, AAC_HWIF_I960RX, 0, AAC_TYPE_SCSI, "Adaptec", "3230S"}, {0x9005, 0x285, 0x9005, 0x289, AAC_HWIF_I960RX, 0, AAC_TYPE_SCSI, "Adaptec", "3240S"}, {0x9005, 0x285, 0x9005, 0x28a, AAC_HWIF_I960RX, 0, AAC_TYPE_SCSI, "Adaptec", "2020ZCR"}, {0x9005, 0x285, 0x9005, 0x28b, AAC_HWIF_I960RX, 0, AAC_TYPE_SCSI, "Adaptec", "2025ZCR"}, {0x9005, 0x286, 0x9005, 0x28c, AAC_HWIF_RKT, 0, AAC_TYPE_SCSI, "Adaptec", "2230S"}, {0x9005, 0x286, 0x9005, 0x28d, AAC_HWIF_RKT, 0, AAC_TYPE_SCSI, "Adaptec", "2130S"}, {0x9005, 0x285, 0x9005, 0x28e, AAC_HWIF_I960RX, 0, AAC_TYPE_SATA, "Adaptec", "2020SA"}, {0x9005, 0x285, 0x9005, 0x28f, AAC_HWIF_I960RX, 0, AAC_TYPE_SATA, "Adaptec", "2025SA"}, {0x9005, 0x285, 0x9005, 0x290, AAC_HWIF_I960RX, AAC_FLAGS_17SG, AAC_TYPE_SATA, "Adaptec", "2410SA"}, {0x9005, 0x285, 0x9005, 0x292, AAC_HWIF_I960RX, AAC_FLAGS_17SG, AAC_TYPE_SATA, "Adaptec", "2810SA"}, {0x9005, 0x285, 0x9005, 0x293, AAC_HWIF_I960RX, AAC_FLAGS_17SG, AAC_TYPE_SATA, "Adaptec", "21610SA"}, {0x9005, 0x285, 0x9005, 0x294, AAC_HWIF_I960RX, 0, AAC_TYPE_SATA, "Adaptec", "2026ZCR"}, {0x9005, 0x285, 0x9005, 0x296, AAC_HWIF_I960RX, 0, AAC_TYPE_SCSI, "Adaptec", "2240S"}, {0x9005, 0x285, 0x9005, 0x297, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "Adaptec", "4005SAS"}, {0x9005, 0x285, 0x9005, 0x298, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "Adaptec", "RAID 4000"}, {0x9005, 0x285, 0x9005, 0x299, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "Adaptec", "4800SAS"}, {0x9005, 0x285, 0x9005, 0x29a, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "Adaptec", "4805SAS"}, {0x9005, 0x286, 0x9005, 0x29b, AAC_HWIF_RKT, 0, AAC_TYPE_SATA, "Adaptec", "2820SA"}, {0x9005, 0x286, 0x9005, 0x29c, AAC_HWIF_RKT, 0, AAC_TYPE_SATA, "Adaptec", "2620SA"}, {0x9005, 0x286, 0x9005, 0x29d, AAC_HWIF_RKT, 0, AAC_TYPE_SATA, "Adaptec", "2420SA"}, {0x9005, 0x286, 0x9005, 0x29e, AAC_HWIF_RKT, 0, AAC_TYPE_SATA, "ICP", "9024RO"}, {0x9005, 0x286, 0x9005, 0x29f, AAC_HWIF_RKT, 0, AAC_TYPE_SATA, "ICP", "9014RO"}, {0x9005, 0x286, 0x9005, 0x2a0, AAC_HWIF_RKT, 0, AAC_TYPE_SATA, "ICP", "9047MA"}, {0x9005, 0x286, 0x9005, 0x2a1, AAC_HWIF_RKT, 0, AAC_TYPE_SATA, "ICP", "9087MA"}, {0x9005, 0x285, 0x9005, 0x2a4, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "ICP", "9085LI"}, {0x9005, 0x285, 0x9005, 0x2a5, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "ICP", "5085BR"}, {0x9005, 0x286, 0x9005, 0x2a6, AAC_HWIF_RKT, 0, AAC_TYPE_SATA, "ICP", "9067MA"}, {0x9005, 0x285, 0x9005, 0x2b5, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "Adaptec", "RAID 5445"}, {0x9005, 0x285, 0x9005, 0x2b6, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "Adaptec", "RAID 5805"}, {0x9005, 0x285, 0x9005, 0x2b7, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "Adaptec", "RAID 5085"}, {0x9005, 0x285, 0x9005, 0x2b8, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "ICP", "RAID ICP5445SL"}, {0x9005, 0x285, 0x9005, 0x2b9, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "ICP", "RAID ICP5085SL"}, {0x9005, 0x285, 0x9005, 0x2ba, AAC_HWIF_I960RX, 0, AAC_TYPE_SAS, "ICP", "RAID ICP5805SL"}, {0, 0, 0, 0, AAC_HWIF_UNKNOWN, 0, AAC_TYPE_UNKNOWN, "Unknown", "AAC card"}, }; /* * Hardware access functions for i960 based cards */ static struct aac_interface aac_rx_interface = { aac_rx_get_fwstatus, aac_rx_get_mailbox, aac_rx_set_mailbox }; /* * Hardware access functions for Rocket based cards */ static struct aac_interface aac_rkt_interface = { aac_rkt_get_fwstatus, aac_rkt_get_mailbox, aac_rkt_set_mailbox }; ddi_device_acc_attr_t aac_acc_attr = { DDI_DEVICE_ATTR_V1, DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC, DDI_DEFAULT_ACC }; static struct { int size; int notify; } aac_qinfo[] = { {AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL}, {AAC_HOST_HIGH_CMD_ENTRIES, 0}, {AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY}, {AAC_ADAP_HIGH_CMD_ENTRIES, 0}, {AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL}, {AAC_HOST_HIGH_RESP_ENTRIES, 0}, {AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY}, {AAC_ADAP_HIGH_RESP_ENTRIES, 0} }; /* * Default aac dma attributes */ static ddi_dma_attr_t aac_dma_attr = { DMA_ATTR_V0, 0, /* lowest usable address */ 0xffffffffull, /* high DMA address range */ 0xffffffffull, /* DMA counter register */ AAC_DMA_ALIGN, /* DMA address alignment */ 1, /* DMA burstsizes */ 1, /* min effective DMA size */ 0xffffffffull, /* max DMA xfer size */ 0xffffffffull, /* segment boundary */ 1, /* s/g list length */ AAC_BLK_SIZE, /* granularity of device */ 0 /* DMA transfer flags */ }; static int aac_tick = AAC_DEFAULT_TICK; /* tick for the internal timer */ static uint32_t aac_timebase = 0; /* internal timer in seconds */ /* * Warlock directives * * Different variables with the same types have to be protected by the * same mutex; otherwise, warlock will complain with "variables don't * seem to be protected consistently". For example, * aac_softstate::{q_wait, q_comp} are type of aac_cmd_queue, and protected * by aac_softstate::{io_lock, q_comp_mutex} respectively. We have to * declare them as protected explictly at aac_cmd_dequeue(). */ _NOTE(SCHEME_PROTECTS_DATA("unique per pkt", scsi_pkt scsi_cdb scsi_status \ scsi_arq_status scsi_descr_sense_hdr scsi_information_sense_descr \ mode_format mode_geometry mode_header aac_cmd)) _NOTE(SCHEME_PROTECTS_DATA("unique per aac_cmd", aac_fib ddi_dma_cookie_t \ aac_sge)) _NOTE(SCHEME_PROTECTS_DATA("unique per aac_fib", aac_blockread aac_blockwrite \ aac_blockread64 aac_raw_io aac_sg_entry aac_sg_entry64 aac_sg_entryraw \ aac_sg_table aac_srb)) _NOTE(SCHEME_PROTECTS_DATA("unique to sync fib and cdb", scsi_inquiry)) _NOTE(SCHEME_PROTECTS_DATA("stable data", scsi_device scsi_address)) _NOTE(SCHEME_PROTECTS_DATA("unique to scsi_transport", buf)) int _init(void) { int rval = 0; #ifdef DEBUG mutex_init(&aac_prt_mutex, NULL, MUTEX_DRIVER, NULL); #endif DBCALLED(NULL, 1); if ((rval = ddi_soft_state_init((void *)&aac_softstatep, sizeof (struct aac_softstate), 0)) != 0) goto error; if ((rval = scsi_hba_init(&aac_modlinkage)) != 0) { ddi_soft_state_fini((void *)&aac_softstatep); goto error; } if ((rval = mod_install(&aac_modlinkage)) != 0) { ddi_soft_state_fini((void *)&aac_softstatep); scsi_hba_fini(&aac_modlinkage); goto error; } return (rval); error: AACDB_PRINT(NULL, CE_WARN, "Mod init error!"); #ifdef DEBUG mutex_destroy(&aac_prt_mutex); #endif return (rval); } int _info(struct modinfo *modinfop) { DBCALLED(NULL, 1); return (mod_info(&aac_modlinkage, modinfop)); } /* * An HBA driver cannot be unload unless you reboot, * so this function will be of no use. */ int _fini(void) { int rval; DBCALLED(NULL, 1); if ((rval = mod_remove(&aac_modlinkage)) != 0) goto error; scsi_hba_fini(&aac_modlinkage); ddi_soft_state_fini((void *)&aac_softstatep); #ifdef DEBUG mutex_destroy(&aac_prt_mutex); #endif return (0); error: AACDB_PRINT(NULL, CE_WARN, "AAC is busy, cannot unload!"); return (rval); } static int aac_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int instance, i; struct aac_softstate *softs = NULL; int attach_state = 0; char *data; DBCALLED(NULL, 1); switch (cmd) { case DDI_ATTACH: break; case DDI_RESUME: return (DDI_FAILURE); default: return (DDI_FAILURE); } instance = ddi_get_instance(dip); /* Get soft state */ if (ddi_soft_state_zalloc(aac_softstatep, instance) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "Cannot alloc soft state"); goto error; } softs = ddi_get_soft_state(aac_softstatep, instance); attach_state |= AAC_ATTACH_SOFTSTATE_ALLOCED; softs->instance = instance; softs->devinfo_p = dip; softs->buf_dma_attr = softs->addr_dma_attr = aac_dma_attr; softs->addr_dma_attr.dma_attr_granular = 1; softs->acc_attr = aac_acc_attr; softs->reg_attr = aac_acc_attr; softs->card = AAC_UNKNOWN_CARD; #ifdef DEBUG softs->debug_flags = aac_debug_flags; softs->debug_fib_flags = aac_debug_fib_flags; #endif /* Initialize FMA */ aac_fm_init(softs); /* Check the card type */ if (aac_check_card_type(softs) == AACERR) { AACDB_PRINT(softs, CE_WARN, "Card not supported"); goto error; } /* We have found the right card and everything is OK */ attach_state |= AAC_ATTACH_CARD_DETECTED; /* Map PCI mem space */ if (ddi_regs_map_setup(dip, 1, (caddr_t *)&softs->pci_mem_base_vaddr, 0, softs->map_size_min, &softs->reg_attr, &softs->pci_mem_handle) != DDI_SUCCESS) goto error; softs->map_size = softs->map_size_min; attach_state |= AAC_ATTACH_PCI_MEM_MAPPED; AAC_DISABLE_INTR(softs); /* Init mutexes and condvars */ mutex_init(&softs->io_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(softs->intr_pri)); mutex_init(&softs->q_comp_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(softs->intr_pri)); mutex_init(&softs->time_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(softs->intr_pri)); mutex_init(&softs->ev_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(softs->intr_pri)); mutex_init(&softs->aifq_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(softs->intr_pri)); cv_init(&softs->event, NULL, CV_DRIVER, NULL); cv_init(&softs->sync_fib_cv, NULL, CV_DRIVER, NULL); cv_init(&softs->drain_cv, NULL, CV_DRIVER, NULL); cv_init(&softs->event_wait_cv, NULL, CV_DRIVER, NULL); cv_init(&softs->event_disp_cv, NULL, CV_DRIVER, NULL); cv_init(&softs->aifq_cv, NULL, CV_DRIVER, NULL); attach_state |= AAC_ATTACH_KMUTEX_INITED; /* Init the cmd queues */ for (i = 0; i < AAC_CMDQ_NUM; i++) aac_cmd_initq(&softs->q_wait[i]); aac_cmd_initq(&softs->q_busy); aac_cmd_initq(&softs->q_comp); /* Check for legacy device naming support */ softs->legacy = 1; /* default to use legacy name */ if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, "legacy-name-enable", &data) == DDI_SUCCESS)) { if (strcmp(data, "no") == 0) { AACDB_PRINT(softs, CE_NOTE, "legacy-name disabled"); softs->legacy = 0; } ddi_prop_free(data); } /* * Everything has been set up till now, * we will do some common attach. */ mutex_enter(&softs->io_lock); if (aac_common_attach(softs) == AACERR) { mutex_exit(&softs->io_lock); goto error; } mutex_exit(&softs->io_lock); attach_state |= AAC_ATTACH_COMM_SPACE_SETUP; /* Check for buf breakup support */ if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, "breakup-enable", &data) == DDI_SUCCESS)) { if (strcmp(data, "yes") == 0) { AACDB_PRINT(softs, CE_NOTE, "buf breakup enabled"); softs->flags |= AAC_FLAGS_BRKUP; } ddi_prop_free(data); } softs->dma_max = softs->buf_dma_attr.dma_attr_maxxfer; if (softs->flags & AAC_FLAGS_BRKUP) { softs->dma_max = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "dma-max", softs->dma_max); } if (aac_hba_setup(softs) != AACOK) goto error; attach_state |= AAC_ATTACH_SCSI_TRAN_SETUP; /* Create devctl/scsi nodes for cfgadm */ if (ddi_create_minor_node(dip, "devctl", S_IFCHR, INST2DEVCTL(instance), DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "failed to create devctl node"); goto error; } attach_state |= AAC_ATTACH_CREATE_DEVCTL; if (ddi_create_minor_node(dip, "scsi", S_IFCHR, INST2SCSI(instance), DDI_NT_SCSI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "failed to create scsi node"); goto error; } attach_state |= AAC_ATTACH_CREATE_SCSI; /* Create aac node for app. to issue ioctls */ if (ddi_create_minor_node(dip, "aac", S_IFCHR, INST2AAC(instance), DDI_PSEUDO, 0) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "failed to create aac node"); goto error; } /* Common attach is OK, so we are attached! */ softs->state |= AAC_STATE_RUN; /* Create event thread */ softs->fibctx_p = &softs->aifctx; if ((softs->event_thread = thread_create(NULL, 0, aac_event_thread, softs, 0, &p0, TS_RUN, minclsyspri)) == NULL) { AACDB_PRINT(softs, CE_WARN, "aif thread create failed"); softs->state &= ~AAC_STATE_RUN; goto error; } aac_unhold_bus(softs, AAC_IOCMD_SYNC | AAC_IOCMD_ASYNC); /* Create a thread for command timeout */ softs->timeout_id = timeout(aac_timer, (void *)softs, (aac_tick * drv_usectohz(1000000))); /* Common attach is OK, so we are attached! */ ddi_report_dev(dip); AACDB_PRINT(softs, CE_NOTE, "aac attached ok"); return (DDI_SUCCESS); error: if (attach_state & AAC_ATTACH_CREATE_SCSI) ddi_remove_minor_node(dip, "scsi"); if (attach_state & AAC_ATTACH_CREATE_DEVCTL) ddi_remove_minor_node(dip, "devctl"); if (attach_state & AAC_ATTACH_COMM_SPACE_SETUP) aac_common_detach(softs); if (attach_state & AAC_ATTACH_SCSI_TRAN_SETUP) { (void) scsi_hba_detach(dip); scsi_hba_tran_free(AAC_DIP2TRAN(dip)); } if (attach_state & AAC_ATTACH_KMUTEX_INITED) { mutex_destroy(&softs->io_lock); mutex_destroy(&softs->q_comp_mutex); mutex_destroy(&softs->time_mutex); mutex_destroy(&softs->ev_lock); mutex_destroy(&softs->aifq_mutex); cv_destroy(&softs->event); cv_destroy(&softs->sync_fib_cv); cv_destroy(&softs->drain_cv); cv_destroy(&softs->event_wait_cv); cv_destroy(&softs->event_disp_cv); cv_destroy(&softs->aifq_cv); } if (attach_state & AAC_ATTACH_PCI_MEM_MAPPED) ddi_regs_map_free(&softs->pci_mem_handle); aac_fm_fini(softs); if (attach_state & AAC_ATTACH_CARD_DETECTED) softs->card = AACERR; if (attach_state & AAC_ATTACH_SOFTSTATE_ALLOCED) ddi_soft_state_free(aac_softstatep, instance); return (DDI_FAILURE); } static int aac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { scsi_hba_tran_t *tran = AAC_DIP2TRAN(dip); struct aac_softstate *softs = AAC_TRAN2SOFTS(tran); DBCALLED(softs, 1); switch (cmd) { case DDI_DETACH: break; case DDI_SUSPEND: return (DDI_FAILURE); default: return (DDI_FAILURE); } mutex_enter(&softs->io_lock); AAC_DISABLE_INTR(softs); softs->state = AAC_STATE_STOPPED; ddi_remove_minor_node(dip, "aac"); ddi_remove_minor_node(dip, "scsi"); ddi_remove_minor_node(dip, "devctl"); mutex_exit(&softs->io_lock); aac_common_detach(softs); mutex_enter(&softs->io_lock); (void) scsi_hba_detach(dip); scsi_hba_tran_free(tran); mutex_exit(&softs->io_lock); /* Stop timer */ mutex_enter(&softs->time_mutex); if (softs->timeout_id) { timeout_id_t tid = softs->timeout_id; softs->timeout_id = 0; mutex_exit(&softs->time_mutex); (void) untimeout(tid); mutex_enter(&softs->time_mutex); } mutex_exit(&softs->time_mutex); /* Destroy event thread */ mutex_enter(&softs->ev_lock); cv_signal(&softs->event_disp_cv); cv_wait(&softs->event_wait_cv, &softs->ev_lock); mutex_exit(&softs->ev_lock); cv_destroy(&softs->aifq_cv); cv_destroy(&softs->event_disp_cv); cv_destroy(&softs->event_wait_cv); cv_destroy(&softs->drain_cv); cv_destroy(&softs->sync_fib_cv); cv_destroy(&softs->event); mutex_destroy(&softs->aifq_mutex); mutex_destroy(&softs->ev_lock); mutex_destroy(&softs->time_mutex); mutex_destroy(&softs->q_comp_mutex); mutex_destroy(&softs->io_lock); ddi_regs_map_free(&softs->pci_mem_handle); aac_fm_fini(softs); softs->hwif = AAC_HWIF_UNKNOWN; softs->card = AAC_UNKNOWN_CARD; ddi_soft_state_free(aac_softstatep, ddi_get_instance(dip)); return (DDI_SUCCESS); } /*ARGSUSED*/ static int aac_reset(dev_info_t *dip, ddi_reset_cmd_t cmd) { struct aac_softstate *softs = AAC_DIP2SOFTS(dip); DBCALLED(softs, 1); mutex_enter(&softs->io_lock); AAC_DISABLE_INTR(softs); (void) aac_shutdown(softs); mutex_exit(&softs->io_lock); return (DDI_SUCCESS); } /* * quiesce(9E) entry point. * * This function is called when the system is single-threaded at high * PIL with preemption disabled. Therefore, this function must not be * blocked. * * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. * DDI_FAILURE indicates an error condition and should almost never happen. */ static int aac_quiesce(dev_info_t *dip) { struct aac_softstate *softs = AAC_DIP2SOFTS(dip); if (softs == NULL) return (DDI_FAILURE); _NOTE(ASSUMING_PROTECTED(softs->state)) AAC_DISABLE_INTR(softs); return (DDI_SUCCESS); } /* ARGSUSED */ static int aac_getinfo(dev_info_t *self, ddi_info_cmd_t infocmd, void *arg, void **result) { int error = DDI_SUCCESS; switch (infocmd) { case DDI_INFO_DEVT2INSTANCE: *result = (void *)(intptr_t)(MINOR2INST(getminor((dev_t)arg))); break; default: error = DDI_FAILURE; } return (error); } /* * Bring the controller down to a dormant state and detach all child devices. * This function is called before detach or system shutdown. * Note: we can assume that the q_wait on the controller is empty, as we * won't allow shutdown if any device is open. */ static int aac_shutdown(struct aac_softstate *softs) { ddi_acc_handle_t acc; struct aac_close_command *cc; int rval; (void) aac_sync_fib_slot_bind(softs, &softs->sync_ac); acc = softs->sync_ac.slotp->fib_acc_handle; cc = (struct aac_close_command *)&softs->sync_ac.slotp->fibp->data[0]; ddi_put32(acc, &cc->Command, VM_CloseAll); ddi_put32(acc, &cc->ContainerId, 0xfffffffful); /* Flush all caches, set FW to write through mode */ rval = aac_sync_fib(softs, ContainerCommand, AAC_FIB_SIZEOF(struct aac_close_command)); aac_sync_fib_slot_release(softs, &softs->sync_ac); AACDB_PRINT(softs, CE_NOTE, "shutting down aac %s", (rval == AACOK) ? "ok" : "fail"); return (rval); } static uint_t aac_softintr(caddr_t arg) { struct aac_softstate *softs = (void *)arg; if (!AAC_IS_Q_EMPTY(&softs->q_comp)) { aac_drain_comp_q(softs); } return (DDI_INTR_CLAIMED); } /* * Setup auto sense data for pkt */ static void aac_set_arq_data(struct scsi_pkt *pkt, uchar_t key, uchar_t add_code, uchar_t qual_code, uint64_t info) { struct scsi_arq_status *arqstat = (void *)(pkt->pkt_scbp); *pkt->pkt_scbp = STATUS_CHECK; /* CHECK CONDITION */ pkt->pkt_state |= STATE_ARQ_DONE; *(uint8_t *)&arqstat->sts_rqpkt_status = STATUS_GOOD; arqstat->sts_rqpkt_reason = CMD_CMPLT; arqstat->sts_rqpkt_resid = 0; arqstat->sts_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD | STATE_XFERRED_DATA; arqstat->sts_rqpkt_statistics = 0; if (info <= 0xfffffffful) { arqstat->sts_sensedata.es_valid = 1; arqstat->sts_sensedata.es_class = CLASS_EXTENDED_SENSE; arqstat->sts_sensedata.es_code = CODE_FMT_FIXED_CURRENT; arqstat->sts_sensedata.es_key = key; arqstat->sts_sensedata.es_add_code = add_code; arqstat->sts_sensedata.es_qual_code = qual_code; arqstat->sts_sensedata.es_info_1 = (info >> 24) & 0xFF; arqstat->sts_sensedata.es_info_2 = (info >> 16) & 0xFF; arqstat->sts_sensedata.es_info_3 = (info >> 8) & 0xFF; arqstat->sts_sensedata.es_info_4 = info & 0xFF; } else { /* 64-bit LBA */ struct scsi_descr_sense_hdr *dsp; struct scsi_information_sense_descr *isd; dsp = (struct scsi_descr_sense_hdr *)&arqstat->sts_sensedata; dsp->ds_class = CLASS_EXTENDED_SENSE; dsp->ds_code = CODE_FMT_DESCR_CURRENT; dsp->ds_key = key; dsp->ds_add_code = add_code; dsp->ds_qual_code = qual_code; dsp->ds_addl_sense_length = sizeof (struct scsi_information_sense_descr); isd = (struct scsi_information_sense_descr *)(dsp+1); isd->isd_descr_type = DESCR_INFORMATION; isd->isd_valid = 1; isd->isd_information[0] = (info >> 56) & 0xFF; isd->isd_information[1] = (info >> 48) & 0xFF; isd->isd_information[2] = (info >> 40) & 0xFF; isd->isd_information[3] = (info >> 32) & 0xFF; isd->isd_information[4] = (info >> 24) & 0xFF; isd->isd_information[5] = (info >> 16) & 0xFF; isd->isd_information[6] = (info >> 8) & 0xFF; isd->isd_information[7] = (info) & 0xFF; } } /* * Setup auto sense data for HARDWARE ERROR */ static void aac_set_arq_data_hwerr(struct aac_cmd *acp) { union scsi_cdb *cdbp; uint64_t err_blkno; cdbp = (void *)acp->pkt->pkt_cdbp; err_blkno = AAC_GETGXADDR(acp->cmdlen, cdbp); aac_set_arq_data(acp->pkt, KEY_HARDWARE_ERROR, 0x00, 0x00, err_blkno); } /* * Send a command to the adapter in New Comm. interface */ static int aac_send_command(struct aac_softstate *softs, struct aac_slot *slotp) { uint32_t index, device; index = PCI_MEM_GET32(softs, AAC_IQUE); if (index == 0xffffffffUL) { index = PCI_MEM_GET32(softs, AAC_IQUE); if (index == 0xffffffffUL) return (AACERR); } device = index; PCI_MEM_PUT32(softs, device, (uint32_t)(slotp->fib_phyaddr & 0xfffffffful)); device += 4; PCI_MEM_PUT32(softs, device, (uint32_t)(slotp->fib_phyaddr >> 32)); device += 4; PCI_MEM_PUT32(softs, device, slotp->acp->fib_size); PCI_MEM_PUT32(softs, AAC_IQUE, index); return (AACOK); } static void aac_end_io(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_device *dvp = acp->dvp; int q = AAC_CMDQ(acp); if (acp->slotp) { /* outstanding cmd */ if (!(acp->flags & AAC_CMD_IN_SYNC_SLOT)) { aac_release_slot(softs, acp->slotp); acp->slotp = NULL; } if (dvp) { dvp->ncmds[q]--; if (dvp->throttle[q] == AAC_THROTTLE_DRAIN && dvp->ncmds[q] == 0 && q == AAC_CMDQ_ASYNC) aac_set_throttle(softs, dvp, q, softs->total_slots); /* * Setup auto sense data for UNIT ATTENTION * Each lun should generate a unit attention * condition when reset. * Phys. drives are treated as logical ones * during error recovery. */ if (dvp->type == AAC_DEV_LD) { struct aac_container *ctp = (struct aac_container *)dvp; if (ctp->reset == 0) goto noreset; AACDB_PRINT(softs, CE_NOTE, "Unit attention: reset"); ctp->reset = 0; aac_set_arq_data(acp->pkt, KEY_UNIT_ATTENTION, 0x29, 0x02, 0); } } noreset: softs->bus_ncmds[q]--; aac_cmd_delete(&softs->q_busy, acp); } else { /* cmd in waiting queue */ aac_cmd_delete(&softs->q_wait[q], acp); } if (!(acp->flags & (AAC_CMD_NO_CB | AAC_CMD_NO_INTR))) { /* async IO */ mutex_enter(&softs->q_comp_mutex); aac_cmd_enqueue(&softs->q_comp, acp); mutex_exit(&softs->q_comp_mutex); } else if (acp->flags & AAC_CMD_NO_CB) { /* sync IO */ cv_broadcast(&softs->event); } } static void aac_handle_io(struct aac_softstate *softs, int index) { struct aac_slot *slotp; struct aac_cmd *acp; uint32_t fast; fast = index & AAC_SENDERADDR_MASK_FAST_RESPONSE; index >>= 2; /* Make sure firmware reported index is valid */ ASSERT(index >= 0 && index < softs->total_slots); slotp = &softs->io_slot[index]; ASSERT(slotp->index == index); acp = slotp->acp; if (acp == NULL || acp->slotp != slotp) { cmn_err(CE_WARN, "Firmware error: invalid slot index received from FW"); return; } acp->flags |= AAC_CMD_CMPLT; (void) ddi_dma_sync(slotp->fib_dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU); if (aac_check_dma_handle(slotp->fib_dma_handle) == DDI_SUCCESS) { /* * For fast response IO, the firmware do not return any FIB * data, so we need to fill in the FIB status and state so that * FIB users can handle it correctly. */ if (fast) { uint32_t state; state = ddi_get32(slotp->fib_acc_handle, &slotp->fibp->Header.XferState); /* * Update state for CPU not for device, no DMA sync * needed */ ddi_put32(slotp->fib_acc_handle, &slotp->fibp->Header.XferState, state | AAC_FIBSTATE_DONEADAP); ddi_put32(slotp->fib_acc_handle, (void *)&slotp->fibp->data[0], ST_OK); } /* Handle completed ac */ acp->ac_comp(softs, acp); } else { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); acp->flags |= AAC_CMD_ERR; if (acp->pkt) { acp->pkt->pkt_reason = CMD_TRAN_ERR; acp->pkt->pkt_statistics = 0; } } aac_end_io(softs, acp); } /* * Interrupt handler for New Comm. interface * New Comm. interface use a different mechanism for interrupt. No explict * message queues, and driver need only accesses the mapped PCI mem space to * find the completed FIB or AIF. */ static int aac_process_intr_new(struct aac_softstate *softs) { uint32_t index; index = AAC_OUTB_GET(softs); if (index == 0xfffffffful) index = AAC_OUTB_GET(softs); if (aac_check_acc_handle(softs->pci_mem_handle) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); return (0); } if (index != 0xfffffffful) { do { if ((index & AAC_SENDERADDR_MASK_AIF) == 0) { aac_handle_io(softs, index); } else if (index != 0xfffffffeul) { struct aac_fib *fibp; /* FIB in AIF queue */ uint16_t fib_size; /* * 0xfffffffe means that the controller wants * more work, ignore it for now. Otherwise, * AIF received. */ index &= ~2; fibp = (struct aac_fib *)(softs-> \ pci_mem_base_vaddr + index); fib_size = PCI_MEM_GET16(softs, index + \ offsetof(struct aac_fib, Header.Size)); aac_save_aif(softs, softs->pci_mem_handle, fibp, fib_size); /* * AIF memory is owned by the adapter, so let it * know that we are done with it. */ AAC_OUTB_SET(softs, index); AAC_STATUS_CLR(softs, AAC_DB_RESPONSE_READY); } index = AAC_OUTB_GET(softs); } while (index != 0xfffffffful); /* * Process waiting cmds before start new ones to * ensure first IOs are serviced first. */ aac_start_waiting_io(softs); return (AAC_DB_COMMAND_READY); } else { return (0); } } static uint_t aac_intr_new(caddr_t arg, caddr_t arg1 __unused) { struct aac_softstate *softs = (void *)arg; uint_t rval; mutex_enter(&softs->io_lock); if (aac_process_intr_new(softs)) rval = DDI_INTR_CLAIMED; else rval = DDI_INTR_UNCLAIMED; mutex_exit(&softs->io_lock); aac_drain_comp_q(softs); return (rval); } /* * Interrupt handler for old interface * Explicit message queues are used to send FIB to and get completed FIB from * the adapter. Driver and adapter maitain the queues in the producer/consumer * manner. The driver has to query the queues to find the completed FIB. */ static int aac_process_intr_old(struct aac_softstate *softs) { uint16_t status; status = AAC_STATUS_GET(softs); if (aac_check_acc_handle(softs->pci_mem_handle) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); return (DDI_INTR_UNCLAIMED); } if (status & AAC_DB_RESPONSE_READY) { int slot_idx; /* ACK the intr */ AAC_STATUS_CLR(softs, AAC_DB_RESPONSE_READY); (void) AAC_STATUS_GET(softs); while (aac_fib_dequeue(softs, AAC_HOST_NORM_RESP_Q, &slot_idx) == AACOK) aac_handle_io(softs, slot_idx); /* * Process waiting cmds before start new ones to * ensure first IOs are serviced first. */ aac_start_waiting_io(softs); return (AAC_DB_RESPONSE_READY); } else if (status & AAC_DB_COMMAND_READY) { int aif_idx; AAC_STATUS_CLR(softs, AAC_DB_COMMAND_READY); (void) AAC_STATUS_GET(softs); if (aac_fib_dequeue(softs, AAC_HOST_NORM_CMD_Q, &aif_idx) == AACOK) { ddi_acc_handle_t acc = softs->comm_space_acc_handle; struct aac_fib *fibp; /* FIB in communication space */ uint16_t fib_size; uint32_t fib_xfer_state; uint32_t addr, size; ASSERT((aif_idx >= 0) && (aif_idx < AAC_ADAPTER_FIBS)); #define AAC_SYNC_AIF(softs, aif_idx, type) \ { (void) ddi_dma_sync((softs)->comm_space_dma_handle, \ offsetof(struct aac_comm_space, \ adapter_fibs[(aif_idx)]), AAC_FIB_SIZE, \ (type)); } /* Copy AIF from adapter to the empty AIF slot */ AAC_SYNC_AIF(softs, aif_idx, DDI_DMA_SYNC_FORCPU); fibp = &softs->comm_space->adapter_fibs[aif_idx]; fib_size = ddi_get16(acc, &fibp->Header.Size); aac_save_aif(softs, acc, fibp, fib_size); /* Complete AIF back to adapter with good status */ fib_xfer_state = LE_32(fibp->Header.XferState); if (fib_xfer_state & AAC_FIBSTATE_FROMADAP) { ddi_put32(acc, &fibp->Header.XferState, fib_xfer_state | AAC_FIBSTATE_DONEHOST); ddi_put32(acc, (void *)&fibp->data[0], ST_OK); if (fib_size > AAC_FIB_SIZE) ddi_put16(acc, &fibp->Header.Size, AAC_FIB_SIZE); AAC_SYNC_AIF(softs, aif_idx, DDI_DMA_SYNC_FORDEV); } /* Put the AIF response on the response queue */ addr = ddi_get32(acc, &softs->comm_space->adapter_fibs[aif_idx]. \ Header.SenderFibAddress); size = (uint32_t)ddi_get16(acc, &softs->comm_space->adapter_fibs[aif_idx]. \ Header.Size); ddi_put32(acc, &softs->comm_space->adapter_fibs[aif_idx]. \ Header.ReceiverFibAddress, addr); if (aac_fib_enqueue(softs, AAC_ADAP_NORM_RESP_Q, addr, size) == AACERR) cmn_err(CE_NOTE, "!AIF ack failed"); } return (AAC_DB_COMMAND_READY); } else if (status & AAC_DB_PRINTF_READY) { /* ACK the intr */ AAC_STATUS_CLR(softs, AAC_DB_PRINTF_READY); (void) AAC_STATUS_GET(softs); (void) ddi_dma_sync(softs->comm_space_dma_handle, offsetof(struct aac_comm_space, adapter_print_buf), AAC_ADAPTER_PRINT_BUFSIZE, DDI_DMA_SYNC_FORCPU); if (aac_check_dma_handle(softs->comm_space_dma_handle) == DDI_SUCCESS) cmn_err(CE_NOTE, "MSG From Adapter: %s", softs->comm_space->adapter_print_buf); else ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); AAC_NOTIFY(softs, AAC_DB_PRINTF_READY); return (AAC_DB_PRINTF_READY); } else if (status & AAC_DB_COMMAND_NOT_FULL) { /* * Without these two condition statements, the OS could hang * after a while, especially if there are a lot of AIF's to * handle, for instance if a drive is pulled from an array * under heavy load. */ AAC_STATUS_CLR(softs, AAC_DB_COMMAND_NOT_FULL); return (AAC_DB_COMMAND_NOT_FULL); } else if (status & AAC_DB_RESPONSE_NOT_FULL) { AAC_STATUS_CLR(softs, AAC_DB_COMMAND_NOT_FULL); AAC_STATUS_CLR(softs, AAC_DB_RESPONSE_NOT_FULL); return (AAC_DB_RESPONSE_NOT_FULL); } else { return (0); } } static uint_t aac_intr_old(caddr_t arg, caddr_t arg1 __unused) { struct aac_softstate *softs = (void *)arg; int rval; mutex_enter(&softs->io_lock); if (aac_process_intr_old(softs)) rval = DDI_INTR_CLAIMED; else rval = DDI_INTR_UNCLAIMED; mutex_exit(&softs->io_lock); aac_drain_comp_q(softs); return (rval); } /* * Query FIXED or MSI interrupts */ static int aac_query_intrs(struct aac_softstate *softs, int intr_type) { dev_info_t *dip = softs->devinfo_p; int avail, actual, count; int i, flag, ret; AACDB_PRINT(softs, CE_NOTE, "aac_query_intrs:interrupt type 0x%x", intr_type); /* Get number of interrupts */ ret = ddi_intr_get_nintrs(dip, intr_type, &count); if ((ret != DDI_SUCCESS) || (count == 0)) { AACDB_PRINT(softs, CE_WARN, "ddi_intr_get_nintrs() failed, ret %d count %d", ret, count); return (DDI_FAILURE); } /* Get number of available interrupts */ ret = ddi_intr_get_navail(dip, intr_type, &avail); if ((ret != DDI_SUCCESS) || (avail == 0)) { AACDB_PRINT(softs, CE_WARN, "ddi_intr_get_navail() failed, ret %d avail %d", ret, avail); return (DDI_FAILURE); } AACDB_PRINT(softs, CE_NOTE, "ddi_intr_get_nvail returned %d, navail() returned %d", count, avail); /* Allocate an array of interrupt handles */ softs->intr_size = count * sizeof (ddi_intr_handle_t); softs->htable = kmem_alloc(softs->intr_size, KM_SLEEP); if (intr_type == DDI_INTR_TYPE_MSI) { count = 1; /* only one vector needed by now */ flag = DDI_INTR_ALLOC_STRICT; } else { /* must be DDI_INTR_TYPE_FIXED */ flag = DDI_INTR_ALLOC_NORMAL; } /* Call ddi_intr_alloc() */ ret = ddi_intr_alloc(dip, softs->htable, intr_type, 0, count, &actual, flag); if ((ret != DDI_SUCCESS) || (actual == 0)) { AACDB_PRINT(softs, CE_WARN, "ddi_intr_alloc() failed, ret = %d", ret); actual = 0; goto error; } if (actual < count) { AACDB_PRINT(softs, CE_NOTE, "Requested: %d, Received: %d", count, actual); goto error; } softs->intr_cnt = actual; /* Get priority for first msi, assume remaining are all the same */ if ((ret = ddi_intr_get_pri(softs->htable[0], &softs->intr_pri)) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "ddi_intr_get_pri() failed, ret = %d", ret); goto error; } /* Test for high level mutex */ if (softs->intr_pri >= ddi_intr_get_hilevel_pri()) { AACDB_PRINT(softs, CE_WARN, "aac_query_intrs: Hi level interrupt not supported"); goto error; } return (DDI_SUCCESS); error: /* Free already allocated intr */ for (i = 0; i < actual; i++) (void) ddi_intr_free(softs->htable[i]); kmem_free(softs->htable, softs->intr_size); return (DDI_FAILURE); } /* * Register FIXED or MSI interrupts, and enable them */ static int aac_add_intrs(struct aac_softstate *softs) { int i, ret; int actual; ddi_intr_handler_t *aac_intr; actual = softs->intr_cnt; aac_intr = ((softs->flags & AAC_FLAGS_NEW_COMM) ? aac_intr_new : aac_intr_old); /* Call ddi_intr_add_handler() */ for (i = 0; i < actual; i++) { if ((ret = ddi_intr_add_handler(softs->htable[i], aac_intr, (caddr_t)softs, NULL)) != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_add_handler() failed ret = %d", ret); /* Free already allocated intr */ for (i = 0; i < actual; i++) (void) ddi_intr_free(softs->htable[i]); kmem_free(softs->htable, softs->intr_size); return (DDI_FAILURE); } } if ((ret = ddi_intr_get_cap(softs->htable[0], &softs->intr_cap)) != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_get_cap() failed, ret = %d", ret); /* Free already allocated intr */ for (i = 0; i < actual; i++) (void) ddi_intr_free(softs->htable[i]); kmem_free(softs->htable, softs->intr_size); return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * Unregister FIXED or MSI interrupts */ static void aac_remove_intrs(struct aac_softstate *softs) { int i; /* Disable all interrupts */ (void) aac_disable_intrs(softs); /* Call ddi_intr_remove_handler() */ for (i = 0; i < softs->intr_cnt; i++) { (void) ddi_intr_remove_handler(softs->htable[i]); (void) ddi_intr_free(softs->htable[i]); } kmem_free(softs->htable, softs->intr_size); } static int aac_enable_intrs(struct aac_softstate *softs) { int rval = AACOK; if (softs->intr_cap & DDI_INTR_FLAG_BLOCK) { /* for MSI block enable */ if (ddi_intr_block_enable(softs->htable, softs->intr_cnt) != DDI_SUCCESS) rval = AACERR; } else { int i; /* Call ddi_intr_enable() for legacy/MSI non block enable */ for (i = 0; i < softs->intr_cnt; i++) { if (ddi_intr_enable(softs->htable[i]) != DDI_SUCCESS) rval = AACERR; } } return (rval); } static int aac_disable_intrs(struct aac_softstate *softs) { int rval = AACOK; if (softs->intr_cap & DDI_INTR_FLAG_BLOCK) { /* Call ddi_intr_block_disable() */ if (ddi_intr_block_disable(softs->htable, softs->intr_cnt) != DDI_SUCCESS) rval = AACERR; } else { int i; for (i = 0; i < softs->intr_cnt; i++) { if (ddi_intr_disable(softs->htable[i]) != DDI_SUCCESS) rval = AACERR; } } return (rval); } /* * Set pkt_reason and OR in pkt_statistics flag */ static void aac_set_pkt_reason(struct aac_softstate *softs, struct aac_cmd *acp, uchar_t reason, uint_t stat) { #ifndef __lock_lint _NOTE(ARGUNUSED(softs)) #endif if (acp->pkt->pkt_reason == CMD_CMPLT) acp->pkt->pkt_reason = reason; acp->pkt->pkt_statistics |= stat; } /* * Handle a finished pkt of soft SCMD */ static void aac_soft_callback(struct aac_softstate *softs, struct aac_cmd *acp) { ASSERT(acp->pkt); acp->flags |= AAC_CMD_CMPLT; acp->pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | \ STATE_SENT_CMD | STATE_GOT_STATUS; if (acp->pkt->pkt_state & STATE_XFERRED_DATA) acp->pkt->pkt_resid = 0; /* AAC_CMD_NO_INTR means no complete callback */ if (!(acp->flags & AAC_CMD_NO_INTR)) { mutex_enter(&softs->q_comp_mutex); aac_cmd_enqueue(&softs->q_comp, acp); mutex_exit(&softs->q_comp_mutex); ddi_trigger_softintr(softs->softint_id); } } /* * Handlers for completed IOs, common to aac_intr_new() and aac_intr_old() */ /* * Handle completed logical device IO command */ /*ARGSUSED*/ static void aac_ld_complete(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_slot *slotp = acp->slotp; struct aac_blockread_response *resp; uint32_t status; ASSERT(!(acp->flags & AAC_CMD_SYNC)); ASSERT(!(acp->flags & AAC_CMD_NO_CB)); acp->pkt->pkt_state |= STATE_GOT_STATUS; /* * block_read/write has a similar response header, use blockread * response for both. */ resp = (struct aac_blockread_response *)&slotp->fibp->data[0]; status = ddi_get32(slotp->fib_acc_handle, &resp->Status); if (status == ST_OK) { acp->pkt->pkt_resid = 0; acp->pkt->pkt_state |= STATE_XFERRED_DATA; } else { aac_set_arq_data_hwerr(acp); } } /* * Handle completed phys. device IO command */ static void aac_pd_complete(struct aac_softstate *softs, struct aac_cmd *acp) { ddi_acc_handle_t acc = acp->slotp->fib_acc_handle; struct aac_fib *fibp = acp->slotp->fibp; struct scsi_pkt *pkt = acp->pkt; struct aac_srb_reply *resp; uint32_t resp_status; ASSERT(!(acp->flags & AAC_CMD_SYNC)); ASSERT(!(acp->flags & AAC_CMD_NO_CB)); resp = (struct aac_srb_reply *)&fibp->data[0]; resp_status = ddi_get32(acc, &resp->status); /* First check FIB status */ if (resp_status == ST_OK) { uint32_t scsi_status; uint32_t srb_status; uint32_t data_xfer_length; scsi_status = ddi_get32(acc, &resp->scsi_status); srb_status = ddi_get32(acc, &resp->srb_status); data_xfer_length = ddi_get32(acc, &resp->data_xfer_length); *pkt->pkt_scbp = (uint8_t)scsi_status; pkt->pkt_state |= STATE_GOT_STATUS; if (scsi_status == STATUS_GOOD) { uchar_t cmd = ((union scsi_cdb *)(void *) (pkt->pkt_cdbp))->scc_cmd; /* Next check SRB status */ switch (srb_status & 0x3f) { case SRB_STATUS_DATA_OVERRUN: AACDB_PRINT(softs, CE_NOTE, "DATA_OVERRUN: " \ "scmd=%d, xfer=%d, buflen=%d", (uint32_t)cmd, data_xfer_length, acp->bcount); switch (cmd) { case SCMD_READ: case SCMD_WRITE: case SCMD_READ_G1: case SCMD_WRITE_G1: case SCMD_READ_G4: case SCMD_WRITE_G4: case SCMD_READ_G5: case SCMD_WRITE_G5: aac_set_pkt_reason(softs, acp, CMD_DATA_OVR, 0); break; } /*FALLTHRU*/ case SRB_STATUS_ERROR_RECOVERY: case SRB_STATUS_PENDING: case SRB_STATUS_SUCCESS: /* * pkt_resid should only be calculated if the * status is ERROR_RECOVERY/PENDING/SUCCESS/ * OVERRUN/UNDERRUN */ if (data_xfer_length) { pkt->pkt_state |= STATE_XFERRED_DATA; pkt->pkt_resid = acp->bcount - \ data_xfer_length; ASSERT(pkt->pkt_resid >= 0); } break; case SRB_STATUS_ABORTED: AACDB_PRINT(softs, CE_NOTE, "SRB_STATUS_ABORTED, xfer=%d, resid=%d", data_xfer_length, pkt->pkt_resid); aac_set_pkt_reason(softs, acp, CMD_ABORTED, STAT_ABORTED); break; case SRB_STATUS_ABORT_FAILED: AACDB_PRINT(softs, CE_NOTE, "SRB_STATUS_ABORT_FAILED, xfer=%d, " \ "resid=%d", data_xfer_length, pkt->pkt_resid); aac_set_pkt_reason(softs, acp, CMD_ABORT_FAIL, 0); break; case SRB_STATUS_PARITY_ERROR: AACDB_PRINT(softs, CE_NOTE, "SRB_STATUS_PARITY_ERROR, xfer=%d, " \ "resid=%d", data_xfer_length, pkt->pkt_resid); aac_set_pkt_reason(softs, acp, CMD_PER_FAIL, 0); break; case SRB_STATUS_NO_DEVICE: case SRB_STATUS_INVALID_PATH_ID: case SRB_STATUS_INVALID_TARGET_ID: case SRB_STATUS_INVALID_LUN: case SRB_STATUS_SELECTION_TIMEOUT: #ifdef DEBUG if (AAC_DEV_IS_VALID(acp->dvp)) { AACDB_PRINT(softs, CE_NOTE, "SRB_STATUS_NO_DEVICE(%d), " \ "xfer=%d, resid=%d ", srb_status & 0x3f, data_xfer_length, pkt->pkt_resid); } #endif aac_set_pkt_reason(softs, acp, CMD_DEV_GONE, 0); break; case SRB_STATUS_COMMAND_TIMEOUT: case SRB_STATUS_TIMEOUT: AACDB_PRINT(softs, CE_NOTE, "SRB_STATUS_COMMAND_TIMEOUT, xfer=%d, " \ "resid=%d", data_xfer_length, pkt->pkt_resid); aac_set_pkt_reason(softs, acp, CMD_TIMEOUT, STAT_TIMEOUT); break; case SRB_STATUS_BUS_RESET: AACDB_PRINT(softs, CE_NOTE, "SRB_STATUS_BUS_RESET, xfer=%d, " \ "resid=%d", data_xfer_length, pkt->pkt_resid); aac_set_pkt_reason(softs, acp, CMD_RESET, STAT_BUS_RESET); break; default: AACDB_PRINT(softs, CE_NOTE, "srb_status=%d, " \ "xfer=%d, resid=%d", srb_status & 0x3f, data_xfer_length, pkt->pkt_resid); aac_set_pkt_reason(softs, acp, CMD_TRAN_ERR, 0); break; } } else if (scsi_status == STATUS_CHECK) { /* CHECK CONDITION */ struct scsi_arq_status *arqstat = (void *)(pkt->pkt_scbp); uint32_t sense_data_size; pkt->pkt_state |= STATE_ARQ_DONE; *(uint8_t *)&arqstat->sts_rqpkt_status = STATUS_GOOD; arqstat->sts_rqpkt_reason = CMD_CMPLT; arqstat->sts_rqpkt_resid = 0; arqstat->sts_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD | STATE_XFERRED_DATA; arqstat->sts_rqpkt_statistics = 0; sense_data_size = ddi_get32(acc, &resp->sense_data_size); ASSERT(sense_data_size <= AAC_SENSE_BUFFERSIZE); AACDB_PRINT(softs, CE_NOTE, "CHECK CONDITION: sense len=%d, xfer len=%d", sense_data_size, data_xfer_length); if (sense_data_size > SENSE_LENGTH) sense_data_size = SENSE_LENGTH; ddi_rep_get8(acc, (uint8_t *)&arqstat->sts_sensedata, (uint8_t *)resp->sense_data, sense_data_size, DDI_DEV_AUTOINCR); } else { AACDB_PRINT(softs, CE_WARN, "invaild scsi status: " \ "scsi_status=%d, srb_status=%d", scsi_status, srb_status); aac_set_pkt_reason(softs, acp, CMD_TRAN_ERR, 0); } } else { AACDB_PRINT(softs, CE_NOTE, "SRB failed: fib status %d", resp_status); aac_set_pkt_reason(softs, acp, CMD_TRAN_ERR, 0); } } /* * Handle completed IOCTL command */ /*ARGSUSED*/ void aac_ioctl_complete(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_slot *slotp = acp->slotp; /* * NOTE: Both aac_ioctl_send_fib() and aac_send_raw_srb() * may wait on softs->event, so use cv_broadcast() instead * of cv_signal(). */ ASSERT(acp->flags & AAC_CMD_SYNC); ASSERT(acp->flags & AAC_CMD_NO_CB); /* Get the size of the response FIB from its FIB.Header.Size field */ acp->fib_size = ddi_get16(slotp->fib_acc_handle, &slotp->fibp->Header.Size); ASSERT(acp->fib_size <= softs->aac_max_fib_size); ddi_rep_get8(slotp->fib_acc_handle, (uint8_t *)acp->fibp, (uint8_t *)slotp->fibp, acp->fib_size, DDI_DEV_AUTOINCR); } /* * Handle completed sync fib command */ /*ARGSUSED*/ void aac_sync_complete(struct aac_softstate *softs, struct aac_cmd *acp) { } /* * Handle completed Flush command */ /*ARGSUSED*/ static void aac_synccache_complete(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_slot *slotp = acp->slotp; ddi_acc_handle_t acc = slotp->fib_acc_handle; struct aac_synchronize_reply *resp; uint32_t status; ASSERT(!(acp->flags & AAC_CMD_SYNC)); acp->pkt->pkt_state |= STATE_GOT_STATUS; resp = (struct aac_synchronize_reply *)&slotp->fibp->data[0]; status = ddi_get32(acc, &resp->Status); if (status != CT_OK) aac_set_arq_data_hwerr(acp); } /*ARGSUSED*/ static void aac_startstop_complete(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_slot *slotp = acp->slotp; ddi_acc_handle_t acc = slotp->fib_acc_handle; struct aac_Container_resp *resp; uint32_t status; ASSERT(!(acp->flags & AAC_CMD_SYNC)); acp->pkt->pkt_state |= STATE_GOT_STATUS; resp = (struct aac_Container_resp *)&slotp->fibp->data[0]; status = ddi_get32(acc, &resp->Status); if (status != 0) { AACDB_PRINT(softs, CE_WARN, "Cannot start/stop a unit"); aac_set_arq_data_hwerr(acp); } } /* * Access PCI space to see if the driver can support the card */ static int aac_check_card_type(struct aac_softstate *softs) { ddi_acc_handle_t pci_config_handle; int card_index; uint32_t pci_cmd; /* Map pci configuration space */ if ((pci_config_setup(softs->devinfo_p, &pci_config_handle)) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "Cannot setup pci config space"); return (AACERR); } softs->vendid = pci_config_get16(pci_config_handle, PCI_CONF_VENID); softs->devid = pci_config_get16(pci_config_handle, PCI_CONF_DEVID); softs->subvendid = pci_config_get16(pci_config_handle, PCI_CONF_SUBVENID); softs->subsysid = pci_config_get16(pci_config_handle, PCI_CONF_SUBSYSID); card_index = 0; while (!CARD_IS_UNKNOWN(card_index)) { if ((aac_cards[card_index].vendor == softs->vendid) && (aac_cards[card_index].device == softs->devid) && (aac_cards[card_index].subvendor == softs->subvendid) && (aac_cards[card_index].subsys == softs->subsysid)) { break; } card_index++; } softs->card = card_index; softs->hwif = aac_cards[card_index].hwif; /* * Unknown aac card * do a generic match based on the VendorID and DeviceID to * support the new cards in the aac family */ if (CARD_IS_UNKNOWN(card_index)) { if (softs->vendid != 0x9005) { AACDB_PRINT(softs, CE_WARN, "Unknown vendor 0x%x", softs->vendid); goto error; } switch (softs->devid) { case 0x285: softs->hwif = AAC_HWIF_I960RX; break; case 0x286: softs->hwif = AAC_HWIF_RKT; break; default: AACDB_PRINT(softs, CE_WARN, "Unknown device \"pci9005,%x\"", softs->devid); goto error; } } /* Set hardware dependent interface */ switch (softs->hwif) { case AAC_HWIF_I960RX: softs->aac_if = aac_rx_interface; softs->map_size_min = AAC_MAP_SIZE_MIN_RX; break; case AAC_HWIF_RKT: softs->aac_if = aac_rkt_interface; softs->map_size_min = AAC_MAP_SIZE_MIN_RKT; break; default: AACDB_PRINT(softs, CE_WARN, "Unknown hardware interface %d", softs->hwif); goto error; } /* Set card names */ (void *)strncpy(softs->vendor_name, aac_cards[card_index].vid, AAC_VENDOR_LEN); (void *)strncpy(softs->product_name, aac_cards[card_index].desc, AAC_PRODUCT_LEN); /* Set up quirks */ softs->flags = aac_cards[card_index].quirks; /* Force the busmaster enable bit on */ pci_cmd = pci_config_get16(pci_config_handle, PCI_CONF_COMM); if ((pci_cmd & PCI_COMM_ME) == 0) { pci_cmd |= PCI_COMM_ME; pci_config_put16(pci_config_handle, PCI_CONF_COMM, pci_cmd); pci_cmd = pci_config_get16(pci_config_handle, PCI_CONF_COMM); if ((pci_cmd & PCI_COMM_ME) == 0) { cmn_err(CE_CONT, "?Cannot enable busmaster bit"); goto error; } } /* Set memory base to map */ softs->pci_mem_base_paddr = 0xfffffff0UL & \ pci_config_get32(pci_config_handle, PCI_CONF_BASE0); pci_config_teardown(&pci_config_handle); return (AACOK); /* card type detected */ error: pci_config_teardown(&pci_config_handle); return (AACERR); /* no matched card found */ } /* * Do the usual interrupt handler setup stuff. */ static int aac_register_intrs(struct aac_softstate *softs) { dev_info_t *dip; int intr_types; ASSERT(softs->devinfo_p); dip = softs->devinfo_p; /* Get the type of device intrrupts */ if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "ddi_intr_get_supported_types() failed"); return (AACERR); } AACDB_PRINT(softs, CE_NOTE, "ddi_intr_get_supported_types() ret: 0x%x", intr_types); /* Query interrupt, and alloc/init all needed struct */ if (intr_types & DDI_INTR_TYPE_MSI) { if (aac_query_intrs(softs, DDI_INTR_TYPE_MSI) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "MSI interrupt query failed"); return (AACERR); } softs->intr_type = DDI_INTR_TYPE_MSI; } else if (intr_types & DDI_INTR_TYPE_FIXED) { if (aac_query_intrs(softs, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "FIXED interrupt query failed"); return (AACERR); } softs->intr_type = DDI_INTR_TYPE_FIXED; } else { AACDB_PRINT(softs, CE_WARN, "Device cannot suppport both FIXED and MSI interrupts"); return (AACERR); } /* Connect interrupt handlers */ if (aac_add_intrs(softs) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "Interrupt registration failed, intr type: %s", softs->intr_type == DDI_INTR_TYPE_MSI ? "MSI" : "FIXED"); return (AACERR); } (void) aac_enable_intrs(softs); if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &softs->softint_id, NULL, NULL, aac_softintr, (caddr_t)softs) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "Can not setup soft interrupt handler!"); aac_remove_intrs(softs); return (AACERR); } return (AACOK); } static void aac_unregister_intrs(struct aac_softstate *softs) { aac_remove_intrs(softs); ddi_remove_softintr(softs->softint_id); } /* * Check the firmware to determine the features to support and the FIB * parameters to use. */ static int aac_check_firmware(struct aac_softstate *softs) { uint32_t options; uint32_t atu_size; ddi_acc_handle_t pci_handle; uint8_t *data; uint32_t max_fibs; uint32_t max_fib_size; uint32_t sg_tablesize; uint32_t max_sectors; uint32_t status; max_fibs = 0; max_sectors = 0; sg_tablesize = 0; /* Get supported options */ if ((aac_sync_mbcommand(softs, AAC_MONKER_GETINFO, 0, 0, 0, 0, &status)) != AACOK) { if (status != SRB_STATUS_INVALID_REQUEST) { cmn_err(CE_CONT, "?Fatal error: request adapter info error"); return (AACERR); } options = 0; atu_size = 0; } else { options = AAC_MAILBOX_GET(softs, 1); atu_size = AAC_MAILBOX_GET(softs, 2); } if (softs->state & AAC_STATE_RESET) { if ((softs->support_opt == options) && (softs->atu_size == atu_size)) return (AACOK); cmn_err(CE_WARN, "?Fatal error: firmware changed, system needs reboot"); return (AACERR); } /* * The following critical settings are initialized only once during * driver attachment. */ softs->support_opt = options; softs->atu_size = atu_size; /* Process supported options */ if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 && (softs->flags & AAC_FLAGS_NO4GB) == 0) { AACDB_PRINT(softs, CE_NOTE, "!Enable FIB map 4GB window"); softs->flags |= AAC_FLAGS_4GB_WINDOW; } else { /* * Quirk AAC_FLAGS_NO4GB is for FIB address and thus comm space * only. IO is handled by the DMA engine which does not suffer * from the ATU window programming workarounds necessary for * CPU copy operations. */ softs->addr_dma_attr.dma_attr_addr_lo = 0x2000ull; softs->addr_dma_attr.dma_attr_addr_hi = 0x7fffffffull; } if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0) { AACDB_PRINT(softs, CE_NOTE, "!Enable SG map 64-bit address"); softs->buf_dma_attr.dma_attr_addr_hi = 0xffffffffffffffffull; softs->buf_dma_attr.dma_attr_seg = 0xffffffffffffffffull; softs->flags |= AAC_FLAGS_SG_64BIT; } if (options & AAC_SUPPORTED_64BIT_ARRAYSIZE) { softs->flags |= AAC_FLAGS_ARRAY_64BIT; AACDB_PRINT(softs, CE_NOTE, "!Enable 64-bit array size"); } if (options & AAC_SUPPORTED_NONDASD) { if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, softs->devinfo_p, 0, "nondasd-enable", (char **)&data) == DDI_SUCCESS)) { if (strcmp((char *)data, "yes") == 0) { AACDB_PRINT(softs, CE_NOTE, "!Enable Non-DASD access"); softs->flags |= AAC_FLAGS_NONDASD; } ddi_prop_free(data); } } /* Read preferred settings */ max_fib_size = 0; if ((aac_sync_mbcommand(softs, AAC_MONKER_GETCOMMPREF, 0, 0, 0, 0, NULL)) == AACOK) { options = AAC_MAILBOX_GET(softs, 1); max_fib_size = (options & 0xffff); max_sectors = (options >> 16) << 1; options = AAC_MAILBOX_GET(softs, 2); sg_tablesize = (options >> 16); options = AAC_MAILBOX_GET(softs, 3); max_fibs = (options & 0xffff); } /* Enable new comm. and rawio at the same time */ if ((softs->support_opt & AAC_SUPPORTED_NEW_COMM) && (max_fib_size != 0)) { /* read out and save PCI MBR */ if ((atu_size > softs->map_size) && (ddi_regs_map_setup(softs->devinfo_p, 1, (caddr_t *)&data, 0, atu_size, &softs->reg_attr, &pci_handle) == DDI_SUCCESS)) { ddi_regs_map_free(&softs->pci_mem_handle); softs->pci_mem_handle = pci_handle; softs->pci_mem_base_vaddr = data; softs->map_size = atu_size; } if (atu_size == softs->map_size) { softs->flags |= AAC_FLAGS_NEW_COMM; AACDB_PRINT(softs, CE_NOTE, "!Enable New Comm. interface"); } } /* Set FIB parameters */ if (softs->flags & AAC_FLAGS_NEW_COMM) { softs->aac_max_fibs = max_fibs; softs->aac_max_fib_size = max_fib_size; softs->aac_max_sectors = max_sectors; softs->aac_sg_tablesize = sg_tablesize; softs->flags |= AAC_FLAGS_RAW_IO; AACDB_PRINT(softs, CE_NOTE, "!Enable RawIO"); } else { softs->aac_max_fibs = (softs->flags & AAC_FLAGS_256FIBS) ? 256 : 512; softs->aac_max_fib_size = AAC_FIB_SIZE; softs->aac_max_sectors = 128; /* 64K */ if (softs->flags & AAC_FLAGS_17SG) softs->aac_sg_tablesize = 17; else if (softs->flags & AAC_FLAGS_34SG) softs->aac_sg_tablesize = 34; else if (softs->flags & AAC_FLAGS_SG_64BIT) softs->aac_sg_tablesize = (AAC_FIB_DATASIZE - sizeof (struct aac_blockwrite64) + sizeof (struct aac_sg_entry64)) / sizeof (struct aac_sg_entry64); else softs->aac_sg_tablesize = (AAC_FIB_DATASIZE - sizeof (struct aac_blockwrite) + sizeof (struct aac_sg_entry)) / sizeof (struct aac_sg_entry); } if ((softs->flags & AAC_FLAGS_RAW_IO) && (softs->flags & AAC_FLAGS_ARRAY_64BIT)) { softs->flags |= AAC_FLAGS_LBA_64BIT; AACDB_PRINT(softs, CE_NOTE, "!Enable 64-bit array"); } softs->buf_dma_attr.dma_attr_sgllen = softs->aac_sg_tablesize; softs->buf_dma_attr.dma_attr_maxxfer = softs->aac_max_sectors << 9; /* * 64K maximum segment size in scatter gather list is controlled by * the NEW_COMM bit in the adapter information. If not set, the card * can only accept a maximum of 64K. It is not recommended to permit * more than 128KB of total transfer size to the adapters because * performance is negatively impacted. * * For new comm, segment size equals max xfer size. For old comm, * we use 64K for both. */ softs->buf_dma_attr.dma_attr_count_max = softs->buf_dma_attr.dma_attr_maxxfer - 1; /* Setup FIB operations */ if (softs->flags & AAC_FLAGS_RAW_IO) softs->aac_cmd_fib = aac_cmd_fib_rawio; else if (softs->flags & AAC_FLAGS_SG_64BIT) softs->aac_cmd_fib = aac_cmd_fib_brw64; else softs->aac_cmd_fib = aac_cmd_fib_brw; softs->aac_cmd_fib_scsi = (softs->flags & AAC_FLAGS_SG_64BIT) ? \ aac_cmd_fib_scsi64 : aac_cmd_fib_scsi32; /* 64-bit LBA needs descriptor format sense data */ softs->slen = sizeof (struct scsi_arq_status); if ((softs->flags & AAC_FLAGS_LBA_64BIT) && softs->slen < AAC_ARQ64_LENGTH) softs->slen = AAC_ARQ64_LENGTH; AACDB_PRINT(softs, CE_NOTE, "!max_fibs %d max_fibsize 0x%x max_sectors %d max_sg %d", softs->aac_max_fibs, softs->aac_max_fib_size, softs->aac_max_sectors, softs->aac_sg_tablesize); return (AACOK); } static void aac_fsa_rev(struct aac_softstate *softs, struct FsaRev *fsarev0, struct FsaRev *fsarev1) { ddi_acc_handle_t acc = softs->sync_ac.slotp->fib_acc_handle; AAC_GET_FIELD8(acc, fsarev1, fsarev0, external.comp.dash); AAC_GET_FIELD8(acc, fsarev1, fsarev0, external.comp.type); AAC_GET_FIELD8(acc, fsarev1, fsarev0, external.comp.minor); AAC_GET_FIELD8(acc, fsarev1, fsarev0, external.comp.major); AAC_GET_FIELD32(acc, fsarev1, fsarev0, buildNumber); } /* * The following function comes from Adaptec: * * Query adapter information and supplement adapter information */ static int aac_get_adapter_info(struct aac_softstate *softs, struct aac_adapter_info *ainfr, struct aac_supplement_adapter_info *sinfr) { struct aac_cmd *acp = &softs->sync_ac; ddi_acc_handle_t acc; struct aac_fib *fibp; struct aac_adapter_info *ainfp; struct aac_supplement_adapter_info *sinfp; int rval; (void) aac_sync_fib_slot_bind(softs, acp); acc = acp->slotp->fib_acc_handle; fibp = acp->slotp->fibp; ddi_put8(acc, &fibp->data[0], 0); if (aac_sync_fib(softs, RequestAdapterInfo, AAC_FIB_SIZEOF(struct aac_adapter_info)) != AACOK) { AACDB_PRINT(softs, CE_WARN, "RequestAdapterInfo failed"); rval = AACERR; goto finish; } ainfp = (struct aac_adapter_info *)fibp->data; if (ainfr) { AAC_GET_FIELD32(acc, ainfr, ainfp, SupportedOptions); AAC_GET_FIELD32(acc, ainfr, ainfp, PlatformBase); AAC_GET_FIELD32(acc, ainfr, ainfp, CpuArchitecture); AAC_GET_FIELD32(acc, ainfr, ainfp, CpuVariant); AAC_GET_FIELD32(acc, ainfr, ainfp, ClockSpeed); AAC_GET_FIELD32(acc, ainfr, ainfp, ExecutionMem); AAC_GET_FIELD32(acc, ainfr, ainfp, BufferMem); AAC_GET_FIELD32(acc, ainfr, ainfp, TotalMem); aac_fsa_rev(softs, &ainfp->KernelRevision, &ainfr->KernelRevision); aac_fsa_rev(softs, &ainfp->MonitorRevision, &ainfr->MonitorRevision); aac_fsa_rev(softs, &ainfp->HardwareRevision, &ainfr->HardwareRevision); aac_fsa_rev(softs, &ainfp->BIOSRevision, &ainfr->BIOSRevision); AAC_GET_FIELD32(acc, ainfr, ainfp, ClusteringEnabled); AAC_GET_FIELD32(acc, ainfr, ainfp, ClusterChannelMask); AAC_GET_FIELD64(acc, ainfr, ainfp, SerialNumber); AAC_GET_FIELD32(acc, ainfr, ainfp, batteryPlatform); AAC_GET_FIELD32(acc, ainfr, ainfp, SupportedOptions); AAC_GET_FIELD32(acc, ainfr, ainfp, OemVariant); } if (sinfr) { if (!(softs->support_opt & AAC_SUPPORTED_SUPPLEMENT_ADAPTER_INFO)) { AACDB_PRINT(softs, CE_WARN, "SupplementAdapterInfo not supported"); rval = AACERR; goto finish; } ddi_put8(acc, &fibp->data[0], 0); if (aac_sync_fib(softs, RequestSupplementAdapterInfo, AAC_FIB_SIZEOF(struct aac_supplement_adapter_info)) != AACOK) { AACDB_PRINT(softs, CE_WARN, "RequestSupplementAdapterInfo failed"); rval = AACERR; goto finish; } sinfp = (struct aac_supplement_adapter_info *)fibp->data; AAC_REP_GET_FIELD8(acc, sinfr, sinfp, AdapterTypeText[0], 17+1); AAC_REP_GET_FIELD8(acc, sinfr, sinfp, Pad[0], 2); AAC_GET_FIELD32(acc, sinfr, sinfp, FlashMemoryByteSize); AAC_GET_FIELD32(acc, sinfr, sinfp, FlashImageId); AAC_GET_FIELD32(acc, sinfr, sinfp, MaxNumberPorts); AAC_GET_FIELD32(acc, sinfr, sinfp, Version); AAC_GET_FIELD32(acc, sinfr, sinfp, FeatureBits); AAC_GET_FIELD8(acc, sinfr, sinfp, SlotNumber); AAC_REP_GET_FIELD8(acc, sinfr, sinfp, ReservedPad0[0], 3); AAC_REP_GET_FIELD8(acc, sinfr, sinfp, BuildDate[0], 12); AAC_GET_FIELD32(acc, sinfr, sinfp, CurrentNumberPorts); AAC_REP_GET_FIELD8(acc, sinfr, sinfp, VpdInfo, sizeof (struct vpd_info)); aac_fsa_rev(softs, &sinfp->FlashFirmwareRevision, &sinfr->FlashFirmwareRevision); AAC_GET_FIELD32(acc, sinfr, sinfp, RaidTypeMorphOptions); aac_fsa_rev(softs, &sinfp->FlashFirmwareBootRevision, &sinfr->FlashFirmwareBootRevision); AAC_REP_GET_FIELD8(acc, sinfr, sinfp, MfgPcbaSerialNo, MFG_PCBA_SERIAL_NUMBER_WIDTH); AAC_REP_GET_FIELD8(acc, sinfr, sinfp, MfgWWNName[0], MFG_WWN_WIDTH); AAC_GET_FIELD32(acc, sinfr, sinfp, SupportedOptions2); AAC_GET_FIELD32(acc, sinfr, sinfp, ExpansionFlag); if (sinfr->ExpansionFlag == 1) { AAC_GET_FIELD32(acc, sinfr, sinfp, FeatureBits3); AAC_GET_FIELD32(acc, sinfr, sinfp, SupportedPerformanceMode); AAC_REP_GET_FIELD32(acc, sinfr, sinfp, ReservedGrowth[0], 80); } } rval = AACOK; finish: aac_sync_fib_slot_release(softs, acp); return (rval); } static int aac_get_bus_info(struct aac_softstate *softs, uint32_t *bus_max, uint32_t *tgt_max) { struct aac_cmd *acp = &softs->sync_ac; ddi_acc_handle_t acc; struct aac_fib *fibp; struct aac_ctcfg *c_cmd; struct aac_ctcfg_resp *c_resp; uint32_t scsi_method_id; struct aac_bus_info *cmd; struct aac_bus_info_response *resp; int rval; (void) aac_sync_fib_slot_bind(softs, acp); acc = acp->slotp->fib_acc_handle; fibp = acp->slotp->fibp; /* Detect MethodId */ c_cmd = (struct aac_ctcfg *)&fibp->data[0]; ddi_put32(acc, &c_cmd->Command, VM_ContainerConfig); ddi_put32(acc, &c_cmd->cmd, CT_GET_SCSI_METHOD); ddi_put32(acc, &c_cmd->param, 0); rval = aac_sync_fib(softs, ContainerCommand, AAC_FIB_SIZEOF(struct aac_ctcfg)); c_resp = (struct aac_ctcfg_resp *)&fibp->data[0]; if (rval != AACOK || ddi_get32(acc, &c_resp->Status) != 0) { AACDB_PRINT(softs, CE_WARN, "VM_ContainerConfig command fail"); rval = AACERR; goto finish; } scsi_method_id = ddi_get32(acc, &c_resp->param); /* Detect phys. bus count and max. target id first */ cmd = (struct aac_bus_info *)&fibp->data[0]; ddi_put32(acc, &cmd->Command, VM_Ioctl); ddi_put32(acc, &cmd->ObjType, FT_DRIVE); /* physical drive */ ddi_put32(acc, &cmd->MethodId, scsi_method_id); ddi_put32(acc, &cmd->ObjectId, 0); ddi_put32(acc, &cmd->CtlCmd, GetBusInfo); /* * For VM_Ioctl, the firmware uses the Header.Size filled from the * driver as the size to be returned. Therefore the driver has to use * sizeof (struct aac_bus_info_response) because it is greater than * sizeof (struct aac_bus_info). */ rval = aac_sync_fib(softs, ContainerCommand, AAC_FIB_SIZEOF(struct aac_bus_info_response)); resp = (struct aac_bus_info_response *)cmd; /* Scan all coordinates with INQUIRY */ if ((rval != AACOK) || (ddi_get32(acc, &resp->Status) != 0)) { AACDB_PRINT(softs, CE_WARN, "GetBusInfo command fail"); rval = AACERR; goto finish; } *bus_max = ddi_get32(acc, &resp->BusCount); *tgt_max = ddi_get32(acc, &resp->TargetsPerBus); finish: aac_sync_fib_slot_release(softs, acp); return (AACOK); } /* * The following function comes from Adaptec: * * Routine to be called during initialization of communications with * the adapter to handle possible adapter configuration issues. When * the adapter first boots up, it examines attached drives, etc, and * potentially comes up with a new or revised configuration (relative to * what's stored in it's NVRAM). Additionally it may discover problems * that make the current physical configuration unworkable (currently * applicable only to cluster configuration issues). * * If there are no configuration issues or the issues are considered * trival by the adapter, it will set it's configuration status to * "FSACT_CONTINUE" and execute the "commit confiuguration" action * automatically on it's own. * * However, if there are non-trivial issues, the adapter will set it's * internal configuration status to "FSACT_PAUSE" or "FASCT_ABORT" * and wait for some agent on the host to issue the "\ContainerCommand * \VM_ContainerConfig\CT_COMMIT_CONFIG" FIB command to cause the * adapter to commit the new/updated configuration and enable * un-inhibited operation. The host agent should first issue the * "\ContainerCommand\VM_ContainerConfig\CT_GET_CONFIG_STATUS" FIB * command to obtain information about config issues detected by * the adapter. * * Normally the adapter's PC BIOS will execute on the host following * adapter poweron and reset and will be responsible for querring the * adapter with CT_GET_CONFIG_STATUS and issuing the CT_COMMIT_CONFIG * command if appropriate. * * However, with the introduction of IOP reset support, the adapter may * boot up without the benefit of the adapter's PC BIOS host agent. * This routine is intended to take care of these issues in situations * where BIOS doesn't execute following adapter poweron or reset. The * CT_COMMIT_CONFIG command is a no-op if it's already been issued, so * there is no harm in doing this when it's already been done. */ static int aac_handle_adapter_config_issues(struct aac_softstate *softs) { struct aac_cmd *acp = &softs->sync_ac; ddi_acc_handle_t acc; struct aac_fib *fibp; struct aac_Container *cmd; struct aac_Container_resp *resp; struct aac_cf_status_header *cfg_sts_hdr; uint32_t resp_status; uint32_t ct_status; uint32_t cfg_stat_action; int rval; (void) aac_sync_fib_slot_bind(softs, acp); acc = acp->slotp->fib_acc_handle; fibp = acp->slotp->fibp; /* Get adapter config status */ cmd = (struct aac_Container *)&fibp->data[0]; bzero(cmd, sizeof (*cmd) - CT_PACKET_SIZE); ddi_put32(acc, &cmd->Command, VM_ContainerConfig); ddi_put32(acc, &cmd->CTCommand.command, CT_GET_CONFIG_STATUS); ddi_put32(acc, &cmd->CTCommand.param[CNT_SIZE], sizeof (struct aac_cf_status_header)); rval = aac_sync_fib(softs, ContainerCommand, AAC_FIB_SIZEOF(struct aac_Container)); resp = (struct aac_Container_resp *)cmd; cfg_sts_hdr = (struct aac_cf_status_header *)resp->CTResponse.data; resp_status = ddi_get32(acc, &resp->Status); ct_status = ddi_get32(acc, &resp->CTResponse.param[0]); if ((rval == AACOK) && (resp_status == 0) && (ct_status == CT_OK)) { cfg_stat_action = ddi_get32(acc, &cfg_sts_hdr->action); /* Commit configuration if it's reasonable to do so. */ if (cfg_stat_action <= CFACT_PAUSE) { bzero(cmd, sizeof (*cmd) - CT_PACKET_SIZE); ddi_put32(acc, &cmd->Command, VM_ContainerConfig); ddi_put32(acc, &cmd->CTCommand.command, CT_COMMIT_CONFIG); rval = aac_sync_fib(softs, ContainerCommand, AAC_FIB_SIZEOF(struct aac_Container)); resp_status = ddi_get32(acc, &resp->Status); ct_status = ddi_get32(acc, &resp->CTResponse.param[0]); if ((rval == AACOK) && (resp_status == 0) && (ct_status == CT_OK)) /* Successful completion */ rval = AACMPE_OK; else /* Auto-commit aborted due to error(s). */ rval = AACMPE_COMMIT_CONFIG; } else { /* * Auto-commit aborted due to adapter indicating * configuration issue(s) too dangerous to auto-commit. */ rval = AACMPE_CONFIG_STATUS; } } else { cmn_err(CE_WARN, "!Configuration issue, auto-commit aborted"); rval = AACMPE_CONFIG_STATUS; } aac_sync_fib_slot_release(softs, acp); return (rval); } /* * Hardware initialization and resource allocation */ static int aac_common_attach(struct aac_softstate *softs) { uint32_t status; int i; struct aac_supplement_adapter_info sinf; DBCALLED(softs, 1); /* * Do a little check here to make sure there aren't any outstanding * FIBs in the message queue. At this point there should not be and * if there are they are probably left over from another instance of * the driver like when the system crashes and the crash dump driver * gets loaded. */ while (AAC_OUTB_GET(softs) != 0xfffffffful) ; /* * Wait the card to complete booting up before do anything that * attempts to communicate with it. */ status = AAC_FWSTATUS_GET(softs); if (status == AAC_SELF_TEST_FAILED || status == AAC_KERNEL_PANIC) goto error; i = AAC_FWUP_TIMEOUT * 1000; /* set timeout */ AAC_BUSYWAIT(AAC_FWSTATUS_GET(softs) & AAC_KERNEL_UP_AND_RUNNING, i); if (i == 0) { cmn_err(CE_CONT, "?Fatal error: controller not ready"); aac_fm_ereport(softs, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); goto error; } /* Read and set card supported options and settings */ if (aac_check_firmware(softs) == AACERR) { aac_fm_ereport(softs, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); goto error; } /* Add interrupt handlers */ if (aac_register_intrs(softs) == AACERR) { cmn_err(CE_CONT, "?Fatal error: interrupts register failed"); goto error; } /* Setup communication space with the card */ if (softs->comm_space_dma_handle == NULL) { if (aac_alloc_comm_space(softs) != AACOK) goto error; } if (aac_setup_comm_space(softs) != AACOK) { cmn_err(CE_CONT, "?Setup communication space failed"); aac_fm_ereport(softs, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); goto error; } #ifdef DEBUG if (aac_get_fw_debug_buffer(softs) != AACOK) cmn_err(CE_CONT, "?firmware UART trace not supported"); #endif /* Allocate slots */ if ((softs->total_slots == 0) && (aac_create_slots(softs) != AACOK)) { cmn_err(CE_CONT, "?Fatal error: slots allocate failed"); goto error; } AACDB_PRINT(softs, CE_NOTE, "%d slots allocated", softs->total_slots); /* Allocate FIBs */ if (softs->total_fibs < softs->total_slots) { aac_alloc_fibs(softs); if (softs->total_fibs == 0) goto error; AACDB_PRINT(softs, CE_NOTE, "%d fibs allocated", softs->total_fibs); } AAC_STATUS_CLR(softs, ~0); /* Clear out all interrupts */ AAC_ENABLE_INTR(softs); /* Enable the interrupts we can handle */ if (aac_get_adapter_info(softs, NULL, &sinf) == AACOK) { softs->feature_bits = sinf.FeatureBits; softs->support_opt2 = sinf.SupportedOptions2; /* Get adapter names */ if (CARD_IS_UNKNOWN(softs->card)) { char *p, *p0, *p1; /* * Now find the controller name in supp_adapter_info-> * AdapterTypeText. Use the first word as the vendor * and the other words as the product name. */ AACDB_PRINT(softs, CE_NOTE, "sinf.AdapterTypeText = " "\"%s\"", sinf.AdapterTypeText); p = sinf.AdapterTypeText; p0 = p1 = NULL; /* Skip heading spaces */ while (*p && (*p == ' ' || *p == '\t')) p++; p0 = p; while (*p && (*p != ' ' && *p != '\t')) p++; /* Remove middle spaces */ while (*p && (*p == ' ' || *p == '\t')) *p++ = 0; p1 = p; /* Remove trailing spaces */ p = p1 + strlen(p1) - 1; while (p > p1 && (*p == ' ' || *p == '\t')) *p-- = 0; if (*p0 && *p1) { (void *)strncpy(softs->vendor_name, p0, AAC_VENDOR_LEN); (void *)strncpy(softs->product_name, p1, AAC_PRODUCT_LEN); } else { cmn_err(CE_WARN, "?adapter name mis-formatted\n"); if (*p0) (void *)strncpy(softs->product_name, p0, AAC_PRODUCT_LEN); } } } else { cmn_err(CE_CONT, "?Query adapter information failed"); } cmn_err(CE_NOTE, "!aac driver %d.%02d.%02d-%d, found card: " \ "%s %s(pci0x%x.%x.%x.%x) at 0x%x", AAC_DRIVER_MAJOR_VERSION, AAC_DRIVER_MINOR_VERSION, AAC_DRIVER_BUGFIX_LEVEL, AAC_DRIVER_BUILD, softs->vendor_name, softs->product_name, softs->vendid, softs->devid, softs->subvendid, softs->subsysid, softs->pci_mem_base_paddr); /* Perform acceptance of adapter-detected config changes if possible */ if (aac_handle_adapter_config_issues(softs) != AACMPE_OK) { cmn_err(CE_CONT, "?Handle adapter config issues failed"); aac_fm_ereport(softs, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); goto error; } /* Setup containers (logical devices) */ if (aac_probe_containers(softs) != AACOK) { cmn_err(CE_CONT, "?Fatal error: get container info error"); goto error; } /* Check for JBOD support. Default disable */ char *data; if (softs->feature_bits & AAC_FEATURE_SUPPORTED_JBOD) { if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, softs->devinfo_p, 0, "jbod-enable", &data) == DDI_SUCCESS)) { if (strcmp(data, "yes") == 0) { AACDB_PRINT(softs, CE_NOTE, "Enable JBOD access"); softs->flags |= AAC_FLAGS_JBOD; } ddi_prop_free(data); } } /* Setup phys. devices */ if (softs->flags & (AAC_FLAGS_NONDASD | AAC_FLAGS_JBOD)) { uint32_t bus_max, tgt_max; uint32_t bus, tgt; int index; if (aac_get_bus_info(softs, &bus_max, &tgt_max) != AACOK) { cmn_err(CE_CONT, "?Fatal error: get bus info error"); goto error; } AACDB_PRINT(softs, CE_NOTE, "bus_max=%d, tgt_max=%d", bus_max, tgt_max); if (bus_max != softs->bus_max || tgt_max != softs->tgt_max) { if (softs->state & AAC_STATE_RESET) { cmn_err(CE_WARN, "?Fatal error: bus map changed"); goto error; } softs->bus_max = bus_max; softs->tgt_max = tgt_max; if (softs->nondasds) { kmem_free(softs->nondasds, AAC_MAX_PD(softs) * \ sizeof (struct aac_nondasd)); } softs->nondasds = kmem_zalloc(AAC_MAX_PD(softs) * \ sizeof (struct aac_nondasd), KM_SLEEP); index = 0; for (bus = 0; bus < softs->bus_max; bus++) { for (tgt = 0; tgt < softs->tgt_max; tgt++) { struct aac_nondasd *dvp = &softs->nondasds[index++]; dvp->dev.type = AAC_DEV_PD; dvp->bus = bus; dvp->tid = tgt; } } } } /* Check dma & acc handles allocated in attach */ if (aac_check_dma_handle(softs->comm_space_dma_handle) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); goto error; } if (aac_check_acc_handle(softs->pci_mem_handle) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); goto error; } for (i = 0; i < softs->total_slots; i++) { if (aac_check_dma_handle(softs->io_slot[i].fib_dma_handle) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); goto error; } } return (AACOK); error: if (softs->state & AAC_STATE_RESET) return (AACERR); if (softs->nondasds) { kmem_free(softs->nondasds, AAC_MAX_PD(softs) * \ sizeof (struct aac_nondasd)); softs->nondasds = NULL; } if (softs->total_fibs > 0) aac_destroy_fibs(softs); if (softs->total_slots > 0) aac_destroy_slots(softs); if (softs->comm_space_dma_handle) aac_free_comm_space(softs); return (AACERR); } /* * Hardware shutdown and resource release */ static void aac_common_detach(struct aac_softstate *softs) { DBCALLED(softs, 1); aac_unregister_intrs(softs); mutex_enter(&softs->io_lock); (void) aac_shutdown(softs); if (softs->nondasds) { kmem_free(softs->nondasds, AAC_MAX_PD(softs) * \ sizeof (struct aac_nondasd)); softs->nondasds = NULL; } aac_destroy_fibs(softs); aac_destroy_slots(softs); aac_free_comm_space(softs); mutex_exit(&softs->io_lock); } /* * Send a synchronous command to the controller and wait for a result. * Indicate if the controller completed the command with an error status. */ int aac_sync_mbcommand(struct aac_softstate *softs, uint32_t cmd, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t *statusp) { int timeout; uint32_t status; if (statusp != NULL) *statusp = SRB_STATUS_SUCCESS; /* Fill in mailbox */ AAC_MAILBOX_SET(softs, cmd, arg0, arg1, arg2, arg3); /* Ensure the sync command doorbell flag is cleared */ AAC_STATUS_CLR(softs, AAC_DB_SYNC_COMMAND); /* Then set it to signal the adapter */ AAC_NOTIFY(softs, AAC_DB_SYNC_COMMAND); /* Spin waiting for the command to complete */ timeout = AAC_IMMEDIATE_TIMEOUT * 1000; AAC_BUSYWAIT(AAC_STATUS_GET(softs) & AAC_DB_SYNC_COMMAND, timeout); if (!timeout) { AACDB_PRINT(softs, CE_WARN, "Sync command timed out after %d seconds (0x%x)!", AAC_IMMEDIATE_TIMEOUT, AAC_FWSTATUS_GET(softs)); return (AACERR); } /* Clear the completion flag */ AAC_STATUS_CLR(softs, AAC_DB_SYNC_COMMAND); /* Get the command status */ status = AAC_MAILBOX_GET(softs, 0); if (statusp != NULL) *statusp = status; if (status != SRB_STATUS_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "Sync command fail: status = 0x%x", status); return (AACERR); } return (AACOK); } /* * Send a synchronous FIB to the adapter and wait for its completion */ static int aac_sync_fib(struct aac_softstate *softs, uint16_t cmd, uint16_t fibsize) { struct aac_cmd *acp = &softs->sync_ac; acp->flags = AAC_CMD_SYNC | AAC_CMD_IN_SYNC_SLOT; if (softs->state & AAC_STATE_INTR) acp->flags |= AAC_CMD_NO_CB; else acp->flags |= AAC_CMD_NO_INTR; acp->ac_comp = aac_sync_complete; acp->timeout = AAC_SYNC_TIMEOUT; acp->fib_size = fibsize; /* * Only need to setup sync fib header, caller should have init * fib data */ aac_cmd_fib_header(softs, acp, cmd); (void) ddi_dma_sync(acp->slotp->fib_dma_handle, 0, fibsize, DDI_DMA_SYNC_FORDEV); aac_start_io(softs, acp); if (softs->state & AAC_STATE_INTR) return (aac_do_sync_io(softs, acp)); else return (aac_do_poll_io(softs, acp)); } static void aac_cmd_initq(struct aac_cmd_queue *q) { q->q_head = NULL; q->q_tail = (struct aac_cmd *)&q->q_head; } /* * Remove a cmd from the head of q */ static struct aac_cmd * aac_cmd_dequeue(struct aac_cmd_queue *q) { struct aac_cmd *acp; _NOTE(ASSUMING_PROTECTED(*q)) if ((acp = q->q_head) != NULL) { if ((q->q_head = acp->next) != NULL) acp->next = NULL; else q->q_tail = (struct aac_cmd *)&q->q_head; acp->prev = NULL; } return (acp); } /* * Add a cmd to the tail of q */ static void aac_cmd_enqueue(struct aac_cmd_queue *q, struct aac_cmd *acp) { ASSERT(acp->next == NULL); acp->prev = q->q_tail; q->q_tail->next = acp; q->q_tail = acp; } /* * Remove the cmd ac from q */ static void aac_cmd_delete(struct aac_cmd_queue *q, struct aac_cmd *acp) { if (acp->prev) { if ((acp->prev->next = acp->next) != NULL) { acp->next->prev = acp->prev; acp->next = NULL; } else { q->q_tail = acp->prev; } acp->prev = NULL; } /* ac is not in the queue */ } /* * Atomically insert an entry into the nominated queue, returns 0 on success or * AACERR if the queue is full. * * Note: it would be more efficient to defer notifying the controller in * the case where we may be inserting several entries in rapid succession, * but implementing this usefully may be difficult (it would involve a * separate queue/notify interface). */ static int aac_fib_enqueue(struct aac_softstate *softs, int queue, uint32_t fib_addr, uint32_t fib_size) { ddi_dma_handle_t dma = softs->comm_space_dma_handle; ddi_acc_handle_t acc = softs->comm_space_acc_handle; uint32_t pi, ci; DBCALLED(softs, 2); ASSERT(queue == AAC_ADAP_NORM_CMD_Q || queue == AAC_ADAP_NORM_RESP_Q); /* Get the producer/consumer indices */ (void) ddi_dma_sync(dma, (uintptr_t)softs->qtablep->qt_qindex[queue] - \ (uintptr_t)softs->comm_space, sizeof (uint32_t) * 2, DDI_DMA_SYNC_FORCPU); if (aac_check_dma_handle(dma) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); return (AACERR); } pi = ddi_get32(acc, &softs->qtablep->qt_qindex[queue][AAC_PRODUCER_INDEX]); ci = ddi_get32(acc, &softs->qtablep->qt_qindex[queue][AAC_CONSUMER_INDEX]); /* * Wrap the queue first before we check the queue to see * if it is full */ if (pi >= aac_qinfo[queue].size) pi = 0; /* XXX queue full */ if ((pi + 1) == ci) return (AACERR); /* Fill in queue entry */ ddi_put32(acc, &((softs->qentries[queue] + pi)->aq_fib_size), fib_size); ddi_put32(acc, &((softs->qentries[queue] + pi)->aq_fib_addr), fib_addr); (void) ddi_dma_sync(dma, (uintptr_t)(softs->qentries[queue] + pi) - \ (uintptr_t)softs->comm_space, sizeof (struct aac_queue_entry), DDI_DMA_SYNC_FORDEV); /* Update producer index */ ddi_put32(acc, &softs->qtablep->qt_qindex[queue][AAC_PRODUCER_INDEX], pi + 1); (void) ddi_dma_sync(dma, (uintptr_t)&softs->qtablep->qt_qindex[queue][AAC_PRODUCER_INDEX] - \ (uintptr_t)softs->comm_space, sizeof (uint32_t), DDI_DMA_SYNC_FORDEV); if (aac_qinfo[queue].notify != 0) AAC_NOTIFY(softs, aac_qinfo[queue].notify); return (AACOK); } /* * Atomically remove one entry from the nominated queue, returns 0 on * success or AACERR if the queue is empty. */ static int aac_fib_dequeue(struct aac_softstate *softs, int queue, int *idxp) { ddi_acc_handle_t acc = softs->comm_space_acc_handle; ddi_dma_handle_t dma = softs->comm_space_dma_handle; uint32_t pi, ci; int unfull = 0; DBCALLED(softs, 2); ASSERT(idxp); /* Get the producer/consumer indices */ (void) ddi_dma_sync(dma, (uintptr_t)softs->qtablep->qt_qindex[queue] - \ (uintptr_t)softs->comm_space, sizeof (uint32_t) * 2, DDI_DMA_SYNC_FORCPU); pi = ddi_get32(acc, &softs->qtablep->qt_qindex[queue][AAC_PRODUCER_INDEX]); ci = ddi_get32(acc, &softs->qtablep->qt_qindex[queue][AAC_CONSUMER_INDEX]); /* Check for queue empty */ if (ci == pi) return (AACERR); if (pi >= aac_qinfo[queue].size) pi = 0; /* Check for queue full */ if (ci == pi + 1) unfull = 1; /* * The controller does not wrap the queue, * so we have to do it by ourselves */ if (ci >= aac_qinfo[queue].size) ci = 0; /* Fetch the entry */ (void) ddi_dma_sync(dma, (uintptr_t)(softs->qentries[queue] + pi) - \ (uintptr_t)softs->comm_space, sizeof (struct aac_queue_entry), DDI_DMA_SYNC_FORCPU); if (aac_check_dma_handle(dma) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); return (AACERR); } switch (queue) { case AAC_HOST_NORM_RESP_Q: case AAC_HOST_HIGH_RESP_Q: *idxp = ddi_get32(acc, &(softs->qentries[queue] + ci)->aq_fib_addr); break; case AAC_HOST_NORM_CMD_Q: case AAC_HOST_HIGH_CMD_Q: *idxp = ddi_get32(acc, &(softs->qentries[queue] + ci)->aq_fib_addr) / AAC_FIB_SIZE; break; default: cmn_err(CE_NOTE, "!Invalid queue in aac_fib_dequeue()"); return (AACERR); } /* Update consumer index */ ddi_put32(acc, &softs->qtablep->qt_qindex[queue][AAC_CONSUMER_INDEX], ci + 1); (void) ddi_dma_sync(dma, (uintptr_t)&softs->qtablep->qt_qindex[queue][AAC_CONSUMER_INDEX] - \ (uintptr_t)softs->comm_space, sizeof (uint32_t), DDI_DMA_SYNC_FORDEV); if (unfull && aac_qinfo[queue].notify != 0) AAC_NOTIFY(softs, aac_qinfo[queue].notify); return (AACOK); } static struct aac_mntinforesp * aac_get_mntinfo(struct aac_softstate *softs, int cid) { ddi_acc_handle_t acc = softs->sync_ac.slotp->fib_acc_handle; struct aac_fib *fibp = softs->sync_ac.slotp->fibp; struct aac_mntinfo *mi = (struct aac_mntinfo *)&fibp->data[0]; struct aac_mntinforesp *mir; ddi_put32(acc, &mi->Command, /* Use 64-bit LBA if enabled */ (softs->flags & AAC_FLAGS_LBA_64BIT) ? VM_NameServe64 : VM_NameServe); ddi_put32(acc, &mi->MntType, FT_FILESYS); ddi_put32(acc, &mi->MntCount, cid); if (aac_sync_fib(softs, ContainerCommand, AAC_FIB_SIZEOF(struct aac_mntinfo)) == AACERR) { AACDB_PRINT(softs, CE_WARN, "Error probe container %d", cid); return (NULL); } mir = (struct aac_mntinforesp *)&fibp->data[0]; if (ddi_get32(acc, &mir->Status) == ST_OK) return (mir); return (NULL); } static int aac_get_container_count(struct aac_softstate *softs, int *count) { ddi_acc_handle_t acc; struct aac_mntinforesp *mir; int rval; (void) aac_sync_fib_slot_bind(softs, &softs->sync_ac); acc = softs->sync_ac.slotp->fib_acc_handle; if ((mir = aac_get_mntinfo(softs, 0)) == NULL) { rval = AACERR; goto finish; } *count = ddi_get32(acc, &mir->MntRespCount); if (*count > AAC_MAX_LD) { AACDB_PRINT(softs, CE_CONT, "container count(%d) > AAC_MAX_LD", *count); rval = AACERR; goto finish; } rval = AACOK; finish: aac_sync_fib_slot_release(softs, &softs->sync_ac); return (rval); } static int aac_get_container_uid(struct aac_softstate *softs, uint32_t cid, uint32_t *uid) { ddi_acc_handle_t acc = softs->sync_ac.slotp->fib_acc_handle; struct aac_Container *ct = (struct aac_Container *) \ &softs->sync_ac.slotp->fibp->data[0]; bzero(ct, sizeof (*ct) - CT_PACKET_SIZE); ddi_put32(acc, &ct->Command, VM_ContainerConfig); ddi_put32(acc, &ct->CTCommand.command, CT_CID_TO_32BITS_UID); ddi_put32(acc, &ct->CTCommand.param[0], cid); if (aac_sync_fib(softs, ContainerCommand, AAC_FIB_SIZEOF(struct aac_Container)) == AACERR) return (AACERR); if (ddi_get32(acc, &ct->CTCommand.param[0]) != CT_OK) return (AACERR); *uid = ddi_get32(acc, &ct->CTCommand.param[1]); return (AACOK); } /* * Request information of the container cid */ static struct aac_mntinforesp * aac_get_container_info(struct aac_softstate *softs, int cid) { ddi_acc_handle_t acc = softs->sync_ac.slotp->fib_acc_handle; struct aac_mntinforesp *mir; int rval_uid; uint32_t uid; /* Get container UID first so that it will not overwrite mntinfo */ rval_uid = aac_get_container_uid(softs, cid, &uid); /* Get container basic info */ if ((mir = aac_get_mntinfo(softs, cid)) == NULL) { AACDB_PRINT(softs, CE_CONT, "query container %d info failed", cid); return (NULL); } if (ddi_get32(acc, &mir->MntObj.VolType) == CT_NONE) return (mir); if (rval_uid != AACOK) { AACDB_PRINT(softs, CE_CONT, "query container %d uid failed", cid); return (NULL); } ddi_put32(acc, &mir->Status, uid); return (mir); } static enum aac_cfg_event aac_probe_container(struct aac_softstate *softs, uint32_t cid) { enum aac_cfg_event event = AAC_CFG_NULL_NOEXIST; struct aac_container *dvp = &softs->containers[cid]; struct aac_mntinforesp *mir; ddi_acc_handle_t acc; (void) aac_sync_fib_slot_bind(softs, &softs->sync_ac); acc = softs->sync_ac.slotp->fib_acc_handle; /* Get container basic info */ if ((mir = aac_get_container_info(softs, cid)) == NULL) { /* AAC_CFG_NULL_NOEXIST */ goto finish; } if (ddi_get32(acc, &mir->MntObj.VolType) == CT_NONE) { if (AAC_DEV_IS_VALID(&dvp->dev)) { AACDB_PRINT(softs, CE_NOTE, ">>> Container %d deleted", cid); dvp->dev.flags &= ~AAC_DFLAG_VALID; event = AAC_CFG_DELETE; } /* AAC_CFG_NULL_NOEXIST */ } else { uint64_t size; uint32_t uid; event = AAC_CFG_NULL_EXIST; size = AAC_MIR_SIZE(softs, acc, mir); uid = ddi_get32(acc, &mir->Status); if (AAC_DEV_IS_VALID(&dvp->dev)) { if (dvp->uid != uid) { AACDB_PRINT(softs, CE_WARN, ">>> Container %u uid changed to %d", cid, uid); dvp->uid = uid; event = AAC_CFG_CHANGE; } if (dvp->size != size) { AACDB_PRINT(softs, CE_NOTE, ">>> Container %u size changed to %"PRIu64, cid, size); dvp->size = size; event = AAC_CFG_CHANGE; } } else { /* Init new container */ AACDB_PRINT(softs, CE_NOTE, ">>> Container %d added: " \ "size=0x%x.%08x, type=%d, name=%s", cid, ddi_get32(acc, &mir->MntObj.CapacityHigh), ddi_get32(acc, &mir->MntObj.Capacity), ddi_get32(acc, &mir->MntObj.VolType), mir->MntObj.FileSystemName); dvp->dev.flags |= AAC_DFLAG_VALID; dvp->dev.type = AAC_DEV_LD; dvp->cid = cid; dvp->uid = uid; dvp->size = size; dvp->locked = 0; dvp->deleted = 0; event = AAC_CFG_ADD; } } finish: aac_sync_fib_slot_release(softs, &softs->sync_ac); return (event); } /* * Do a rescan of all the possible containers and update the container list * with newly online/offline containers, and prepare for autoconfiguration. */ static int aac_probe_containers(struct aac_softstate *softs) { int i, count, total; /* Loop over possible containers */ count = softs->container_count; if (aac_get_container_count(softs, &count) == AACERR) return (AACERR); for (i = total = 0; i < count; i++) { enum aac_cfg_event event = aac_probe_container(softs, i); if ((event != AAC_CFG_NULL_NOEXIST) && (event != AAC_CFG_NULL_EXIST)) { (void) aac_handle_dr(softs, i, -1, event); total++; } } if (count < softs->container_count) { struct aac_container *dvp; for (dvp = &softs->containers[count]; dvp < &softs->containers[softs->container_count]; dvp++) { if (!AAC_DEV_IS_VALID(&dvp->dev)) continue; AACDB_PRINT(softs, CE_NOTE, ">>> Container %d deleted", dvp->cid); dvp->dev.flags &= ~AAC_DFLAG_VALID; (void) aac_handle_dr(softs, dvp->cid, -1, AAC_CFG_DELETE); } } softs->container_count = count; AACDB_PRINT(softs, CE_CONT, "?Total %d container(s) found", total); return (AACOK); } static int aac_probe_jbod(struct aac_softstate *softs, int tgt, int event) { ASSERT(AAC_MAX_LD <= tgt); ASSERT(tgt < AAC_MAX_DEV(softs)); struct aac_device *dvp; dvp = AAC_DEV(softs, tgt); switch (event) { case AAC_CFG_ADD: AACDB_PRINT(softs, CE_NOTE, ">>> Jbod %d added", tgt - AAC_MAX_LD); dvp->flags |= AAC_DFLAG_VALID; dvp->type = AAC_DEV_PD; break; case AAC_CFG_DELETE: AACDB_PRINT(softs, CE_NOTE, ">>> Jbod %d deleted", tgt - AAC_MAX_LD); dvp->flags &= ~AAC_DFLAG_VALID; break; default: return (AACERR); } (void) aac_handle_dr(softs, tgt, 0, event); return (AACOK); } static int aac_alloc_comm_space(struct aac_softstate *softs) { size_t rlen; ddi_dma_cookie_t cookie; uint_t cookien; /* Allocate DMA for comm. space */ if (ddi_dma_alloc_handle( softs->devinfo_p, &softs->addr_dma_attr, DDI_DMA_SLEEP, NULL, &softs->comm_space_dma_handle) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "Cannot alloc dma handle for communication area"); goto error; } if (ddi_dma_mem_alloc( softs->comm_space_dma_handle, sizeof (struct aac_comm_space), &softs->acc_attr, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, (caddr_t *)&softs->comm_space, &rlen, &softs->comm_space_acc_handle) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "Cannot alloc mem for communication area"); goto error; } if (ddi_dma_addr_bind_handle( softs->comm_space_dma_handle, NULL, (caddr_t)softs->comm_space, sizeof (struct aac_comm_space), DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &cookien) != DDI_DMA_MAPPED) { AACDB_PRINT(softs, CE_WARN, "DMA bind failed for communication area"); goto error; } softs->comm_space_phyaddr = cookie.dmac_address; return (AACOK); error: if (softs->comm_space_acc_handle) { ddi_dma_mem_free(&softs->comm_space_acc_handle); softs->comm_space_acc_handle = NULL; } if (softs->comm_space_dma_handle) { ddi_dma_free_handle(&softs->comm_space_dma_handle); softs->comm_space_dma_handle = NULL; } return (AACERR); } static void aac_free_comm_space(struct aac_softstate *softs) { (void) ddi_dma_unbind_handle(softs->comm_space_dma_handle); ddi_dma_mem_free(&softs->comm_space_acc_handle); softs->comm_space_acc_handle = NULL; ddi_dma_free_handle(&softs->comm_space_dma_handle); softs->comm_space_dma_handle = NULL; softs->comm_space_phyaddr = 0; } /* * Initialize the data structures that are required for the communication * interface to operate */ static int aac_setup_comm_space(struct aac_softstate *softs) { ddi_dma_handle_t dma = softs->comm_space_dma_handle; ddi_acc_handle_t acc = softs->comm_space_acc_handle; uint32_t comm_space_phyaddr; struct aac_adapter_init *initp; int qoffset; comm_space_phyaddr = softs->comm_space_phyaddr; /* Setup adapter init struct */ initp = &softs->comm_space->init_data; bzero(initp, sizeof (struct aac_adapter_init)); ddi_put32(acc, &initp->InitStructRevision, AAC_INIT_STRUCT_REVISION); ddi_put32(acc, &initp->HostElapsedSeconds, ddi_get_time()); /* Setup new/old comm. specific data */ if (softs->flags & AAC_FLAGS_RAW_IO) { uint32_t init_flags = 0; if (softs->flags & AAC_FLAGS_NEW_COMM) init_flags |= AAC_INIT_FLAGS_NEW_COMM_SUPPORTED; /* AAC_SUPPORTED_POWER_MANAGEMENT */ init_flags |= AAC_INIT_FLAGS_DRIVER_SUPPORTS_PM; init_flags |= AAC_INIT_FLAGS_DRIVER_USES_UTC_TIME; ddi_put32(acc, &initp->InitStructRevision, AAC_INIT_STRUCT_REVISION_4); ddi_put32(acc, &initp->InitFlags, init_flags); /* Setup the preferred settings */ ddi_put32(acc, &initp->MaxIoCommands, softs->aac_max_fibs); ddi_put32(acc, &initp->MaxIoSize, (softs->aac_max_sectors << 9)); ddi_put32(acc, &initp->MaxFibSize, softs->aac_max_fib_size); } else { /* * Tells the adapter about the physical location of various * important shared data structures */ ddi_put32(acc, &initp->AdapterFibsPhysicalAddress, comm_space_phyaddr + \ offsetof(struct aac_comm_space, adapter_fibs)); ddi_put32(acc, &initp->AdapterFibsVirtualAddress, 0); ddi_put32(acc, &initp->AdapterFibAlign, AAC_FIB_SIZE); ddi_put32(acc, &initp->AdapterFibsSize, AAC_ADAPTER_FIBS * AAC_FIB_SIZE); ddi_put32(acc, &initp->PrintfBufferAddress, comm_space_phyaddr + \ offsetof(struct aac_comm_space, adapter_print_buf)); ddi_put32(acc, &initp->PrintfBufferSize, AAC_ADAPTER_PRINT_BUFSIZE); ddi_put32(acc, &initp->MiniPortRevision, AAC_INIT_STRUCT_MINIPORT_REVISION); ddi_put32(acc, &initp->HostPhysMemPages, AAC_MAX_PFN); qoffset = (comm_space_phyaddr + \ offsetof(struct aac_comm_space, qtable)) % \ AAC_QUEUE_ALIGN; if (qoffset) qoffset = AAC_QUEUE_ALIGN - qoffset; softs->qtablep = (struct aac_queue_table *) \ ((char *)&softs->comm_space->qtable + qoffset); ddi_put32(acc, &initp->CommHeaderAddress, comm_space_phyaddr + \ offsetof(struct aac_comm_space, qtable) + qoffset); /* Init queue table */ ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_HOST_NORM_CMD_Q][AAC_PRODUCER_INDEX], AAC_HOST_NORM_CMD_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_HOST_NORM_CMD_Q][AAC_CONSUMER_INDEX], AAC_HOST_NORM_CMD_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_HOST_HIGH_CMD_Q][AAC_PRODUCER_INDEX], AAC_HOST_HIGH_CMD_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_HOST_HIGH_CMD_Q][AAC_CONSUMER_INDEX], AAC_HOST_HIGH_CMD_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_ADAP_NORM_CMD_Q][AAC_PRODUCER_INDEX], AAC_ADAP_NORM_CMD_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_ADAP_NORM_CMD_Q][AAC_CONSUMER_INDEX], AAC_ADAP_NORM_CMD_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_ADAP_HIGH_CMD_Q][AAC_PRODUCER_INDEX], AAC_ADAP_HIGH_CMD_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_ADAP_HIGH_CMD_Q][AAC_CONSUMER_INDEX], AAC_ADAP_HIGH_CMD_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_HOST_NORM_RESP_Q][AAC_PRODUCER_INDEX], AAC_HOST_NORM_RESP_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_HOST_NORM_RESP_Q][AAC_CONSUMER_INDEX], AAC_HOST_NORM_RESP_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_HOST_HIGH_RESP_Q][AAC_PRODUCER_INDEX], AAC_HOST_HIGH_RESP_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_HOST_HIGH_RESP_Q][AAC_CONSUMER_INDEX], AAC_HOST_HIGH_RESP_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_ADAP_NORM_RESP_Q][AAC_PRODUCER_INDEX], AAC_ADAP_NORM_RESP_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_ADAP_NORM_RESP_Q][AAC_CONSUMER_INDEX], AAC_ADAP_NORM_RESP_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_ADAP_HIGH_RESP_Q][AAC_PRODUCER_INDEX], AAC_ADAP_HIGH_RESP_ENTRIES); ddi_put32(acc, &softs->qtablep-> \ qt_qindex[AAC_ADAP_HIGH_RESP_Q][AAC_CONSUMER_INDEX], AAC_ADAP_HIGH_RESP_ENTRIES); /* Init queue entries */ softs->qentries[AAC_HOST_NORM_CMD_Q] = &softs->qtablep->qt_HostNormCmdQueue[0]; softs->qentries[AAC_HOST_HIGH_CMD_Q] = &softs->qtablep->qt_HostHighCmdQueue[0]; softs->qentries[AAC_ADAP_NORM_CMD_Q] = &softs->qtablep->qt_AdapNormCmdQueue[0]; softs->qentries[AAC_ADAP_HIGH_CMD_Q] = &softs->qtablep->qt_AdapHighCmdQueue[0]; softs->qentries[AAC_HOST_NORM_RESP_Q] = &softs->qtablep->qt_HostNormRespQueue[0]; softs->qentries[AAC_HOST_HIGH_RESP_Q] = &softs->qtablep->qt_HostHighRespQueue[0]; softs->qentries[AAC_ADAP_NORM_RESP_Q] = &softs->qtablep->qt_AdapNormRespQueue[0]; softs->qentries[AAC_ADAP_HIGH_RESP_Q] = &softs->qtablep->qt_AdapHighRespQueue[0]; } (void) ddi_dma_sync(dma, 0, 0, DDI_DMA_SYNC_FORDEV); /* Send init structure to the card */ if (aac_sync_mbcommand(softs, AAC_MONKER_INITSTRUCT, comm_space_phyaddr + \ offsetof(struct aac_comm_space, init_data), 0, 0, 0, NULL) == AACERR) { AACDB_PRINT(softs, CE_WARN, "Cannot send init structure to adapter"); return (AACERR); } return (AACOK); } static uchar_t * aac_vendor_id(struct aac_softstate *softs, uchar_t *buf) { (void) memset(buf, ' ', AAC_VENDOR_LEN); bcopy(softs->vendor_name, buf, strlen(softs->vendor_name)); return (buf + AAC_VENDOR_LEN); } static uchar_t * aac_product_id(struct aac_softstate *softs, uchar_t *buf) { (void) memset(buf, ' ', AAC_PRODUCT_LEN); bcopy(softs->product_name, buf, strlen(softs->product_name)); return (buf + AAC_PRODUCT_LEN); } /* * Construct unit serial number from container uid */ static uchar_t * aac_lun_serialno(struct aac_softstate *softs, int tgt, uchar_t *buf) { int i, d; uint32_t uid; ASSERT(tgt >= 0 && tgt < AAC_MAX_LD); uid = softs->containers[tgt].uid; for (i = 7; i >= 0; i--) { d = uid & 0xf; buf[i] = d > 9 ? 'A' + (d - 0xa) : '0' + d; uid >>= 4; } return (buf + 8); } /* * SPC-3 7.5 INQUIRY command implementation */ static void aac_inquiry(struct aac_softstate *softs, struct scsi_pkt *pkt, union scsi_cdb *cdbp, struct buf *bp) { int tgt = pkt->pkt_address.a_target; char *b_addr = NULL; uchar_t page = cdbp->cdb_opaque[2]; if (cdbp->cdb_opaque[1] & AAC_CDB_INQUIRY_CMDDT) { /* Command Support Data is not supported */ aac_set_arq_data(pkt, KEY_ILLEGAL_REQUEST, 0x24, 0x00, 0); return; } if (bp && bp->b_un.b_addr && bp->b_bcount) { if (bp->b_flags & (B_PHYS | B_PAGEIO)) bp_mapin(bp); b_addr = bp->b_un.b_addr; } if (cdbp->cdb_opaque[1] & AAC_CDB_INQUIRY_EVPD) { uchar_t *vpdp = (uchar_t *)b_addr; uchar_t *idp, *sp; /* SPC-3 8.4 Vital product data parameters */ switch (page) { case 0x00: /* Supported VPD pages */ if (vpdp == NULL || bp->b_bcount < (AAC_VPD_PAGE_DATA + 3)) return; bzero(vpdp, AAC_VPD_PAGE_LENGTH); vpdp[AAC_VPD_PAGE_CODE] = 0x00; vpdp[AAC_VPD_PAGE_LENGTH] = 3; vpdp[AAC_VPD_PAGE_DATA] = 0x00; vpdp[AAC_VPD_PAGE_DATA + 1] = 0x80; vpdp[AAC_VPD_PAGE_DATA + 2] = 0x83; pkt->pkt_state |= STATE_XFERRED_DATA; break; case 0x80: /* Unit serial number page */ if (vpdp == NULL || bp->b_bcount < (AAC_VPD_PAGE_DATA + 8)) return; bzero(vpdp, AAC_VPD_PAGE_LENGTH); vpdp[AAC_VPD_PAGE_CODE] = 0x80; vpdp[AAC_VPD_PAGE_LENGTH] = 8; sp = &vpdp[AAC_VPD_PAGE_DATA]; (void) aac_lun_serialno(softs, tgt, sp); pkt->pkt_state |= STATE_XFERRED_DATA; break; case 0x83: /* Device identification page */ if (vpdp == NULL || bp->b_bcount < (AAC_VPD_PAGE_DATA + 32)) return; bzero(vpdp, AAC_VPD_PAGE_LENGTH); vpdp[AAC_VPD_PAGE_CODE] = 0x83; idp = &vpdp[AAC_VPD_PAGE_DATA]; bzero(idp, AAC_VPD_ID_LENGTH); idp[AAC_VPD_ID_CODESET] = 0x02; idp[AAC_VPD_ID_TYPE] = 0x01; /* * SPC-3 Table 111 - Identifier type * One recommanded method of constructing the remainder * of identifier field is to concatenate the product * identification field from the standard INQUIRY data * field and the product serial number field from the * unit serial number page. */ sp = &idp[AAC_VPD_ID_DATA]; sp = aac_vendor_id(softs, sp); sp = aac_product_id(softs, sp); sp = aac_lun_serialno(softs, tgt, sp); idp[AAC_VPD_ID_LENGTH] = (uintptr_t)sp - \ (uintptr_t)&idp[AAC_VPD_ID_DATA]; vpdp[AAC_VPD_PAGE_LENGTH] = (uintptr_t)sp - \ (uintptr_t)&vpdp[AAC_VPD_PAGE_DATA]; pkt->pkt_state |= STATE_XFERRED_DATA; break; default: aac_set_arq_data(pkt, KEY_ILLEGAL_REQUEST, 0x24, 0x00, 0); break; } } else { struct scsi_inquiry *inqp = (struct scsi_inquiry *)b_addr; size_t len = sizeof (struct scsi_inquiry); if (page != 0) { aac_set_arq_data(pkt, KEY_ILLEGAL_REQUEST, 0x24, 0x00, 0); return; } if (inqp == NULL || bp->b_bcount < len) return; bzero(inqp, len); inqp->inq_len = AAC_ADDITIONAL_LEN; inqp->inq_ansi = AAC_ANSI_VER; inqp->inq_rdf = AAC_RESP_DATA_FORMAT; (void) aac_vendor_id(softs, (uchar_t *)inqp->inq_vid); (void) aac_product_id(softs, (uchar_t *)inqp->inq_pid); bcopy("V1.0", inqp->inq_revision, 4); inqp->inq_cmdque = 1; /* enable tagged-queuing */ /* * For "sd-max-xfer-size" property which may impact performance * when IO threads increase. */ inqp->inq_wbus32 = 1; pkt->pkt_state |= STATE_XFERRED_DATA; } } /* * SPC-3 7.10 MODE SENSE command implementation */ static void aac_mode_sense(struct aac_softstate *softs, struct scsi_pkt *pkt, union scsi_cdb *cdbp, struct buf *bp, int capacity) { uchar_t pagecode; struct mode_header *headerp; struct mode_header_g1 *g1_headerp; unsigned int ncyl; caddr_t sense_data; caddr_t next_page; size_t sdata_size; size_t pages_size; int unsupport_page = 0; ASSERT(cdbp->scc_cmd == SCMD_MODE_SENSE || cdbp->scc_cmd == SCMD_MODE_SENSE_G1); if (!(bp && bp->b_un.b_addr && bp->b_bcount)) return; if (bp->b_flags & (B_PHYS | B_PAGEIO)) bp_mapin(bp); pkt->pkt_state |= STATE_XFERRED_DATA; pagecode = cdbp->cdb_un.sg.scsi[0] & 0x3F; /* calculate the size of needed buffer */ if (cdbp->scc_cmd == SCMD_MODE_SENSE) sdata_size = MODE_HEADER_LENGTH; else /* must be SCMD_MODE_SENSE_G1 */ sdata_size = MODE_HEADER_LENGTH_G1; pages_size = 0; switch (pagecode) { case SD_MODE_SENSE_PAGE3_CODE: pages_size += sizeof (struct mode_format); break; case SD_MODE_SENSE_PAGE4_CODE: pages_size += sizeof (struct mode_geometry); break; case MODEPAGE_CTRL_MODE: if (softs->flags & AAC_FLAGS_LBA_64BIT) { pages_size += sizeof (struct mode_control_scsi3); } else { unsupport_page = 1; } break; case MODEPAGE_ALLPAGES: if (softs->flags & AAC_FLAGS_LBA_64BIT) { pages_size += sizeof (struct mode_format) + sizeof (struct mode_geometry) + sizeof (struct mode_control_scsi3); } else { pages_size += sizeof (struct mode_format) + sizeof (struct mode_geometry); } break; default: /* unsupported pages */ unsupport_page = 1; } /* allocate buffer to fill the send data */ sdata_size += pages_size; sense_data = kmem_zalloc(sdata_size, KM_SLEEP); if (cdbp->scc_cmd == SCMD_MODE_SENSE) { headerp = (struct mode_header *)sense_data; headerp->length = MODE_HEADER_LENGTH + pages_size - sizeof (headerp->length); headerp->bdesc_length = 0; next_page = sense_data + sizeof (struct mode_header); } else { g1_headerp = (void *)sense_data; g1_headerp->length = BE_16(MODE_HEADER_LENGTH_G1 + pages_size - sizeof (g1_headerp->length)); g1_headerp->bdesc_length = 0; next_page = sense_data + sizeof (struct mode_header_g1); } if (unsupport_page) goto finish; if (pagecode == SD_MODE_SENSE_PAGE3_CODE || pagecode == MODEPAGE_ALLPAGES) { /* SBC-3 7.1.3.3 Format device page */ struct mode_format *page3p; page3p = (void *)next_page; page3p->mode_page.code = SD_MODE_SENSE_PAGE3_CODE; page3p->mode_page.length = sizeof (struct mode_format); page3p->data_bytes_sect = BE_16(AAC_SECTOR_SIZE); page3p->sect_track = BE_16(AAC_SECTORS_PER_TRACK); next_page += sizeof (struct mode_format); } if (pagecode == SD_MODE_SENSE_PAGE4_CODE || pagecode == MODEPAGE_ALLPAGES) { /* SBC-3 7.1.3.8 Rigid disk device geometry page */ struct mode_geometry *page4p; page4p = (void *)next_page; page4p->mode_page.code = SD_MODE_SENSE_PAGE4_CODE; page4p->mode_page.length = sizeof (struct mode_geometry); page4p->heads = AAC_NUMBER_OF_HEADS; page4p->rpm = BE_16(AAC_ROTATION_SPEED); ncyl = capacity / (AAC_NUMBER_OF_HEADS * AAC_SECTORS_PER_TRACK); page4p->cyl_lb = ncyl & 0xff; page4p->cyl_mb = (ncyl >> 8) & 0xff; page4p->cyl_ub = (ncyl >> 16) & 0xff; next_page += sizeof (struct mode_geometry); } if ((pagecode == MODEPAGE_CTRL_MODE || pagecode == MODEPAGE_ALLPAGES) && softs->flags & AAC_FLAGS_LBA_64BIT) { /* 64-bit LBA need large sense data */ struct mode_control_scsi3 *mctl; mctl = (void *)next_page; mctl->mode_page.code = MODEPAGE_CTRL_MODE; mctl->mode_page.length = sizeof (struct mode_control_scsi3) - sizeof (struct mode_page); mctl->d_sense = 1; } finish: /* copyout the valid data. */ bcopy(sense_data, bp->b_un.b_addr, min(sdata_size, bp->b_bcount)); kmem_free(sense_data, sdata_size); } static int aac_name_node(dev_info_t *dip, char *name, int len) { int tgt, lun; tgt = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "target", -1); if (tgt == -1) return (DDI_FAILURE); lun = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "lun", -1); if (lun == -1) return (DDI_FAILURE); (void) snprintf(name, len, "%x,%x", tgt, lun); return (DDI_SUCCESS); } /*ARGSUSED*/ static int aac_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip, scsi_hba_tran_t *tran, struct scsi_device *sd) { struct aac_softstate *softs = AAC_TRAN2SOFTS(tran); #if defined(DEBUG) || defined(__lock_lint) int ctl = ddi_get_instance(softs->devinfo_p); #endif uint16_t tgt = sd->sd_address.a_target; uint8_t lun = sd->sd_address.a_lun; struct aac_device *dvp; DBCALLED(softs, 2); if (ndi_dev_is_persistent_node(tgt_dip) == 0) { /* * If no persistent node exist, we don't allow .conf node * to be created. */ if (aac_find_child(softs, tgt, lun) != NULL) { if (ndi_merge_node(tgt_dip, aac_name_node) != DDI_SUCCESS) /* Create this .conf node */ return (DDI_SUCCESS); } return (DDI_FAILURE); } /* * Only support container/phys. device that has been * detected and valid */ mutex_enter(&softs->io_lock); if (tgt >= AAC_MAX_DEV(softs)) { AACDB_PRINT_TRAN(softs, "aac_tran_tgt_init: c%dt%dL%d out", ctl, tgt, lun); mutex_exit(&softs->io_lock); return (DDI_FAILURE); } if (tgt < AAC_MAX_LD) { dvp = (struct aac_device *)&softs->containers[tgt]; if (lun != 0 || !AAC_DEV_IS_VALID(dvp)) { AACDB_PRINT_TRAN(softs, "aac_tran_tgt_init: c%dt%dL%d", ctl, tgt, lun); mutex_exit(&softs->io_lock); return (DDI_FAILURE); } /* * Save the tgt_dip for the given target if one doesn't exist * already. Dip's for non-existance tgt's will be cleared in * tgt_free. */ if (softs->containers[tgt].dev.dip == NULL && strcmp(ddi_driver_name(sd->sd_dev), "sd") == 0) softs->containers[tgt].dev.dip = tgt_dip; } else { dvp = (struct aac_device *)&softs->nondasds[AAC_PD(tgt)]; /* * Save the tgt_dip for the given target if one doesn't exist * already. Dip's for non-existance tgt's will be cleared in * tgt_free. */ if (softs->nondasds[AAC_PD(tgt)].dev.dip == NULL && strcmp(ddi_driver_name(sd->sd_dev), "sd") == 0) softs->nondasds[AAC_PD(tgt)].dev.dip = tgt_dip; } if (softs->flags & AAC_FLAGS_BRKUP) { if (ndi_prop_update_int(DDI_DEV_T_NONE, tgt_dip, "buf_break", 1) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, "unable to create " "property for t%dL%d (buf_break)", tgt, lun); } } AACDB_PRINT(softs, CE_NOTE, "aac_tran_tgt_init: c%dt%dL%d ok (%s)", ctl, tgt, lun, (dvp->type == AAC_DEV_PD) ? "pd" : "ld"); mutex_exit(&softs->io_lock); return (DDI_SUCCESS); } static void aac_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip, scsi_hba_tran_t *hba_tran, struct scsi_device *sd) { #ifndef __lock_lint _NOTE(ARGUNUSED(hba_dip, tgt_dip, hba_tran)) #endif struct aac_softstate *softs = SD2AAC(sd); int tgt = sd->sd_address.a_target; mutex_enter(&softs->io_lock); if (tgt < AAC_MAX_LD) { if (softs->containers[tgt].dev.dip == tgt_dip) softs->containers[tgt].dev.dip = NULL; } else { if (softs->nondasds[AAC_PD(tgt)].dev.dip == tgt_dip) softs->nondasds[AAC_PD(tgt)].dev.dip = NULL; softs->nondasds[AAC_PD(tgt)].dev.flags &= ~AAC_DFLAG_VALID; } mutex_exit(&softs->io_lock); } /* * Check if the firmware is Up And Running. If it is in the Kernel Panic * state, (BlinkLED code + 1) is returned. * 0 -- firmware up and running * -1 -- firmware dead * >0 -- firmware kernel panic */ static int aac_check_adapter_health(struct aac_softstate *softs) { int rval; rval = PCI_MEM_GET32(softs, AAC_OMR0); if (rval & AAC_KERNEL_UP_AND_RUNNING) { rval = 0; } else if (rval & AAC_KERNEL_PANIC) { cmn_err(CE_WARN, "firmware panic"); rval = ((rval >> 16) & 0xff) + 1; /* avoid 0 as return value */ } else { cmn_err(CE_WARN, "firmware dead"); rval = -1; } return (rval); } static void aac_abort_iocmd(struct aac_softstate *softs, struct aac_cmd *acp, uchar_t reason) { acp->flags |= AAC_CMD_ABORT; if (acp->pkt) { if (acp->slotp) { /* outstanding cmd */ acp->pkt->pkt_state |= STATE_GOT_STATUS; } switch (reason) { case CMD_TIMEOUT: AACDB_PRINT(softs, CE_NOTE, "CMD_TIMEOUT: acp=0x%p", acp); aac_set_pkt_reason(softs, acp, CMD_TIMEOUT, STAT_TIMEOUT | STAT_BUS_RESET); break; case CMD_RESET: /* aac support only RESET_ALL */ AACDB_PRINT(softs, CE_NOTE, "CMD_RESET: acp=0x%p", acp); aac_set_pkt_reason(softs, acp, CMD_RESET, STAT_BUS_RESET); break; case CMD_ABORTED: AACDB_PRINT(softs, CE_NOTE, "CMD_ABORTED: acp=0x%p", acp); aac_set_pkt_reason(softs, acp, CMD_ABORTED, STAT_ABORTED); break; } } aac_end_io(softs, acp); } /* * Abort all the pending commands of type iocmd or just the command pkt * corresponding to pkt */ static void aac_abort_iocmds(struct aac_softstate *softs, int iocmd, struct scsi_pkt *pkt, int reason) { struct aac_cmd *ac_arg, *acp; int i; if (pkt == NULL) { ac_arg = NULL; } else { ac_arg = PKT2AC(pkt); iocmd = (ac_arg->flags & AAC_CMD_SYNC) ? AAC_IOCMD_SYNC : AAC_IOCMD_ASYNC; } /* * a) outstanding commands on the controller * Note: should abort outstanding commands only after one * IOP reset has been done. */ if (iocmd & AAC_IOCMD_OUTSTANDING) { struct aac_cmd *acp; for (i = 0; i < AAC_MAX_LD; i++) { if (AAC_DEV_IS_VALID(&softs->containers[i].dev)) softs->containers[i].reset = 1; } while ((acp = softs->q_busy.q_head) != NULL) aac_abort_iocmd(softs, acp, reason); } /* b) commands in the waiting queues */ for (i = 0; i < AAC_CMDQ_NUM; i++) { if (iocmd & (1 << i)) { if (ac_arg) { aac_abort_iocmd(softs, ac_arg, reason); } else { while ((acp = softs->q_wait[i].q_head) != NULL) aac_abort_iocmd(softs, acp, reason); } } } } /* * The draining thread is shared among quiesce threads. It terminates * when the adapter is quiesced or stopped by aac_stop_drain(). */ static void aac_check_drain(void *arg) { struct aac_softstate *softs = arg; mutex_enter(&softs->io_lock); if (softs->ndrains) { softs->drain_timeid = 0; /* * If both ASYNC and SYNC bus throttle are held, * wake up threads only when both are drained out. */ if ((softs->bus_throttle[AAC_CMDQ_ASYNC] > 0 || softs->bus_ncmds[AAC_CMDQ_ASYNC] == 0) && (softs->bus_throttle[AAC_CMDQ_SYNC] > 0 || softs->bus_ncmds[AAC_CMDQ_SYNC] == 0)) cv_broadcast(&softs->drain_cv); else softs->drain_timeid = timeout(aac_check_drain, softs, AAC_QUIESCE_TICK * drv_usectohz(1000000)); } mutex_exit(&softs->io_lock); } /* * If not draining the outstanding cmds, drain them. Otherwise, * only update ndrains. */ static void aac_start_drain(struct aac_softstate *softs) { if (softs->ndrains == 0) { ASSERT(softs->drain_timeid == 0); softs->drain_timeid = timeout(aac_check_drain, softs, AAC_QUIESCE_TICK * drv_usectohz(1000000)); } softs->ndrains++; } /* * Stop the draining thread when no other threads use it any longer. * Side effect: io_lock may be released in the middle. */ static void aac_stop_drain(struct aac_softstate *softs) { softs->ndrains--; if (softs->ndrains == 0) { if (softs->drain_timeid != 0) { timeout_id_t tid = softs->drain_timeid; softs->drain_timeid = 0; mutex_exit(&softs->io_lock); (void) untimeout(tid); mutex_enter(&softs->io_lock); } } } /* * The following function comes from Adaptec: * * Once do an IOP reset, basically the driver have to re-initialize the card * as if up from a cold boot, and the driver is responsible for any IO that * is outstanding to the adapter at the time of the IOP RESET. And prepare * for IOP RESET by making the init code modular with the ability to call it * from multiple places. */ static int aac_reset_adapter(struct aac_softstate *softs) { int health; uint32_t status; int rval = AAC_IOP_RESET_FAILED; DBCALLED(softs, 1); ASSERT(softs->state & AAC_STATE_RESET); ddi_fm_acc_err_clear(softs->pci_mem_handle, DDI_FME_VER0); /* Disable interrupt */ AAC_DISABLE_INTR(softs); health = aac_check_adapter_health(softs); if (health == -1) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); goto finish; } if (health == 0) /* flush drives if possible */ (void) aac_shutdown(softs); /* Execute IOP reset */ if ((aac_sync_mbcommand(softs, AAC_IOP_RESET, 0, 0, 0, 0, &status)) != AACOK) { ddi_acc_handle_t acc; struct aac_fib *fibp; struct aac_pause_command *pc; if ((status & 0xf) == 0xf) { uint32_t wait_count; /* * Sunrise Lake has dual cores and we must drag the * other core with us to reset simultaneously. There * are 2 bits in the Inbound Reset Control and Status * Register (offset 0x38) of the Sunrise Lake to reset * the chip without clearing out the PCI configuration * info (COMMAND & BARS). */ PCI_MEM_PUT32(softs, AAC_IRCSR, AAC_IRCSR_CORES_RST); /* * We need to wait for 5 seconds before accessing the MU * again 10000 * 100us = 1000,000us = 1000ms = 1s */ wait_count = 5 * 10000; while (wait_count) { drv_usecwait(100); /* delay 100 microseconds */ wait_count--; } } else { if (status == SRB_STATUS_INVALID_REQUEST) cmn_err(CE_WARN, "!IOP_RESET not supported"); else /* probably timeout */ cmn_err(CE_WARN, "!IOP_RESET failed"); /* Unwind aac_shutdown() */ (void) aac_sync_fib_slot_bind(softs, &softs->sync_ac); acc = softs->sync_ac.slotp->fib_acc_handle; fibp = softs->sync_ac.slotp->fibp; pc = (struct aac_pause_command *)&fibp->data[0]; bzero(pc, sizeof (*pc)); ddi_put32(acc, &pc->Command, VM_ContainerConfig); ddi_put32(acc, &pc->Type, CT_PAUSE_IO); ddi_put32(acc, &pc->Timeout, 1); ddi_put32(acc, &pc->Min, 1); ddi_put32(acc, &pc->NoRescan, 1); (void) aac_sync_fib(softs, ContainerCommand, AAC_FIB_SIZEOF(struct aac_pause_command)); aac_sync_fib_slot_release(softs, &softs->sync_ac); if (aac_check_adapter_health(softs) != 0) ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); else /* * IOP reset not supported or IOP not reseted */ rval = AAC_IOP_RESET_ABNORMAL; goto finish; } } /* * Re-read and renegotiate the FIB parameters, as one of the actions * that can result from an IOP reset is the running of a new firmware * image. */ if (aac_common_attach(softs) != AACOK) goto finish; rval = AAC_IOP_RESET_SUCCEED; finish: AAC_ENABLE_INTR(softs); return (rval); } static void aac_set_throttle(struct aac_softstate *softs, struct aac_device *dvp, int q, int throttle) { /* * If the bus is draining/quiesced, no changes to the throttles * are allowed. All throttles should have been set to 0. */ if ((softs->state & AAC_STATE_QUIESCED) || softs->ndrains) return; dvp->throttle[q] = throttle; } static void aac_hold_bus(struct aac_softstate *softs, int iocmds) { int i, q; /* Hold bus by holding every device on the bus */ for (q = 0; q < AAC_CMDQ_NUM; q++) { if (iocmds & (1 << q)) { softs->bus_throttle[q] = 0; for (i = 0; i < AAC_MAX_LD; i++) aac_set_throttle(softs, &softs->containers[i].dev, q, 0); for (i = 0; i < AAC_MAX_PD(softs); i++) aac_set_throttle(softs, &softs->nondasds[i].dev, q, 0); } } } static void aac_unhold_bus(struct aac_softstate *softs, int iocmds) { int i, q, max_throttle; for (q = 0; q < AAC_CMDQ_NUM; q++) { if (iocmds & (1 << q)) { /* * Should not unhold AAC_IOCMD_ASYNC bus, if it has been * quiesced or being drained by possibly some quiesce * threads. */ if (q == AAC_CMDQ_ASYNC && ((softs->state & AAC_STATE_QUIESCED) || softs->ndrains)) continue; if (q == AAC_CMDQ_ASYNC) max_throttle = softs->total_slots - AAC_MGT_SLOT_NUM; else max_throttle = softs->total_slots - 1; softs->bus_throttle[q] = max_throttle; for (i = 0; i < AAC_MAX_LD; i++) aac_set_throttle(softs, &softs->containers[i].dev, q, max_throttle); for (i = 0; i < AAC_MAX_PD(softs); i++) aac_set_throttle(softs, &softs->nondasds[i].dev, q, max_throttle); } } } static int aac_do_reset(struct aac_softstate *softs) { int health; int rval; softs->state |= AAC_STATE_RESET; health = aac_check_adapter_health(softs); /* * Hold off new io commands and wait all outstanding io * commands to complete. */ if (health == 0) { int sync_cmds = softs->bus_ncmds[AAC_CMDQ_SYNC]; int async_cmds = softs->bus_ncmds[AAC_CMDQ_ASYNC]; if (sync_cmds == 0 && async_cmds == 0) { rval = AAC_IOP_RESET_SUCCEED; goto finish; } /* * Give the adapter up to AAC_QUIESCE_TIMEOUT more seconds * to complete the outstanding io commands */ int timeout = AAC_QUIESCE_TIMEOUT * 1000 * 10; int (*intr_handler)(struct aac_softstate *); aac_hold_bus(softs, AAC_IOCMD_SYNC | AAC_IOCMD_ASYNC); /* * Poll the adapter by ourselves in case interrupt is disabled * and to avoid releasing the io_lock. */ intr_handler = (softs->flags & AAC_FLAGS_NEW_COMM) ? aac_process_intr_new : aac_process_intr_old; while ((softs->bus_ncmds[AAC_CMDQ_SYNC] || softs->bus_ncmds[AAC_CMDQ_ASYNC]) && timeout) { drv_usecwait(100); (void) intr_handler(softs); timeout--; } aac_unhold_bus(softs, AAC_IOCMD_SYNC | AAC_IOCMD_ASYNC); if (softs->bus_ncmds[AAC_CMDQ_SYNC] == 0 && softs->bus_ncmds[AAC_CMDQ_ASYNC] == 0) { /* Cmds drained out */ rval = AAC_IOP_RESET_SUCCEED; goto finish; } else if (softs->bus_ncmds[AAC_CMDQ_SYNC] < sync_cmds || softs->bus_ncmds[AAC_CMDQ_ASYNC] < async_cmds) { /* Cmds not drained out, adapter overloaded */ rval = AAC_IOP_RESET_ABNORMAL; goto finish; } } /* * If a longer waiting time still can't drain any outstanding io * commands, do IOP reset. */ if ((rval = aac_reset_adapter(softs)) == AAC_IOP_RESET_FAILED) softs->state |= AAC_STATE_DEAD; finish: softs->state &= ~AAC_STATE_RESET; return (rval); } static int aac_tran_reset(struct scsi_address *ap, int level) { struct aac_softstate *softs = AAC_TRAN2SOFTS(ap->a_hba_tran); int rval; DBCALLED(softs, 1); if (level != RESET_ALL) { cmn_err(CE_NOTE, "!reset target/lun not supported"); return (0); } mutex_enter(&softs->io_lock); switch (rval = aac_do_reset(softs)) { case AAC_IOP_RESET_SUCCEED: aac_abort_iocmds(softs, AAC_IOCMD_OUTSTANDING | AAC_IOCMD_ASYNC, NULL, CMD_RESET); aac_start_waiting_io(softs); break; case AAC_IOP_RESET_FAILED: /* Abort IOCTL cmds when adapter is dead */ aac_abort_iocmds(softs, AAC_IOCMD_ALL, NULL, CMD_RESET); break; case AAC_IOP_RESET_ABNORMAL: aac_start_waiting_io(softs); } mutex_exit(&softs->io_lock); aac_drain_comp_q(softs); return (rval == 0); } static int aac_tran_abort(struct scsi_address *ap, struct scsi_pkt *pkt) { struct aac_softstate *softs = AAC_TRAN2SOFTS(ap->a_hba_tran); DBCALLED(softs, 1); mutex_enter(&softs->io_lock); aac_abort_iocmds(softs, 0, pkt, CMD_ABORTED); mutex_exit(&softs->io_lock); aac_drain_comp_q(softs); return (1); } void aac_free_dmamap(struct aac_cmd *acp) { /* Free dma mapping */ if (acp->flags & AAC_CMD_DMA_VALID) { ASSERT(acp->buf_dma_handle); (void) ddi_dma_unbind_handle(acp->buf_dma_handle); acp->flags &= ~AAC_CMD_DMA_VALID; } if (acp->abp != NULL) { /* free non-aligned buf DMA */ ASSERT(acp->buf_dma_handle); if ((acp->flags & AAC_CMD_BUF_WRITE) == 0 && acp->bp) ddi_rep_get8(acp->abh, (uint8_t *)acp->bp->b_un.b_addr, (uint8_t *)acp->abp, acp->bp->b_bcount, DDI_DEV_AUTOINCR); ddi_dma_mem_free(&acp->abh); acp->abp = NULL; } if (acp->buf_dma_handle) { ddi_dma_free_handle(&acp->buf_dma_handle); acp->buf_dma_handle = NULL; } } static void aac_unknown_scmd(struct aac_softstate *softs, struct aac_cmd *acp) { AACDB_PRINT(softs, CE_CONT, "SCMD 0x%x not supported", ((union scsi_cdb *)(void *)acp->pkt->pkt_cdbp)->scc_cmd); aac_free_dmamap(acp); aac_set_arq_data(acp->pkt, KEY_ILLEGAL_REQUEST, 0x20, 0x00, 0); aac_soft_callback(softs, acp); } /* * Handle command to logical device */ static int aac_tran_start_ld(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_container *dvp; struct scsi_pkt *pkt; union scsi_cdb *cdbp; struct buf *bp; int rval; dvp = (struct aac_container *)acp->dvp; pkt = acp->pkt; cdbp = (void *)pkt->pkt_cdbp; bp = acp->bp; switch (cdbp->scc_cmd) { case SCMD_INQUIRY: /* inquiry */ aac_free_dmamap(acp); aac_inquiry(softs, pkt, cdbp, bp); aac_soft_callback(softs, acp); rval = TRAN_ACCEPT; break; case SCMD_READ_CAPACITY: /* read capacity */ if (bp && bp->b_un.b_addr && bp->b_bcount) { struct scsi_capacity cap; uint64_t last_lba; /* check 64-bit LBA */ last_lba = dvp->size - 1; if (last_lba > 0xffffffffull) { cap.capacity = 0xfffffffful; } else { cap.capacity = BE_32(last_lba); } cap.lbasize = BE_32(AAC_SECTOR_SIZE); aac_free_dmamap(acp); if (bp->b_flags & (B_PHYS|B_PAGEIO)) bp_mapin(bp); bcopy(&cap, bp->b_un.b_addr, min(bp->b_bcount, 8)); pkt->pkt_state |= STATE_XFERRED_DATA; } aac_soft_callback(softs, acp); rval = TRAN_ACCEPT; break; case SCMD_SVC_ACTION_IN_G4: /* read capacity 16 */ /* Check if containers need 64-bit LBA support */ if (cdbp->cdb_opaque[1] == SSVC_ACTION_READ_CAPACITY_G4) { if (bp && bp->b_un.b_addr && bp->b_bcount) { struct scsi_capacity_16 cap16; int cap_len = sizeof (struct scsi_capacity_16); bzero(&cap16, cap_len); cap16.sc_capacity = BE_64(dvp->size - 1); cap16.sc_lbasize = BE_32(AAC_SECTOR_SIZE); aac_free_dmamap(acp); if (bp->b_flags & (B_PHYS | B_PAGEIO)) bp_mapin(bp); bcopy(&cap16, bp->b_un.b_addr, min(bp->b_bcount, cap_len)); pkt->pkt_state |= STATE_XFERRED_DATA; } aac_soft_callback(softs, acp); } else { aac_unknown_scmd(softs, acp); } rval = TRAN_ACCEPT; break; case SCMD_READ_G4: /* read_16 */ case SCMD_WRITE_G4: /* write_16 */ if (softs->flags & AAC_FLAGS_RAW_IO) { /* NOTE: GETG4ADDRTL(cdbp) is int32_t */ acp->blkno = ((uint64_t) \ GETG4ADDR(cdbp) << 32) | \ (uint32_t)GETG4ADDRTL(cdbp); goto do_io; } AACDB_PRINT(softs, CE_WARN, "64-bit LBA not supported"); aac_unknown_scmd(softs, acp); rval = TRAN_ACCEPT; break; case SCMD_READ: /* read_6 */ case SCMD_WRITE: /* write_6 */ acp->blkno = GETG0ADDR(cdbp); goto do_io; case SCMD_READ_G5: /* read_12 */ case SCMD_WRITE_G5: /* write_12 */ acp->blkno = GETG5ADDR(cdbp); goto do_io; case SCMD_READ_G1: /* read_10 */ case SCMD_WRITE_G1: /* write_10 */ acp->blkno = (uint32_t)GETG1ADDR(cdbp); do_io: if (acp->flags & AAC_CMD_DMA_VALID) { uint64_t cnt_size = dvp->size; /* * If LBA > array size AND rawio, the * adapter may hang. So check it before * sending. * NOTE: (blkno + blkcnt) may overflow */ if ((acp->blkno < cnt_size) && ((acp->blkno + acp->bcount / AAC_BLK_SIZE) <= cnt_size)) { rval = aac_do_io(softs, acp); } else { /* * Request exceeds the capacity of disk, * set error block number to last LBA * + 1. */ aac_set_arq_data(pkt, KEY_ILLEGAL_REQUEST, 0x21, 0x00, cnt_size); aac_soft_callback(softs, acp); rval = TRAN_ACCEPT; } } else if (acp->bcount == 0) { /* For 0 length IO, just return ok */ aac_soft_callback(softs, acp); rval = TRAN_ACCEPT; } else { rval = TRAN_BADPKT; } break; case SCMD_MODE_SENSE: /* mode_sense_6 */ case SCMD_MODE_SENSE_G1: { /* mode_sense_10 */ int capacity; aac_free_dmamap(acp); if (dvp->size > 0xffffffffull) capacity = 0xfffffffful; /* 64-bit LBA */ else capacity = dvp->size; aac_mode_sense(softs, pkt, cdbp, bp, capacity); aac_soft_callback(softs, acp); rval = TRAN_ACCEPT; break; } case SCMD_START_STOP: if (softs->support_opt2 & AAC_SUPPORTED_POWER_MANAGEMENT) { acp->aac_cmd_fib = aac_cmd_fib_startstop; acp->ac_comp = aac_startstop_complete; rval = aac_do_io(softs, acp); break; } /* FALLTHRU */ case SCMD_TEST_UNIT_READY: case SCMD_REQUEST_SENSE: case SCMD_FORMAT: aac_free_dmamap(acp); if (bp && bp->b_un.b_addr && bp->b_bcount) { if (acp->flags & AAC_CMD_BUF_READ) { if (bp->b_flags & (B_PHYS|B_PAGEIO)) bp_mapin(bp); bzero(bp->b_un.b_addr, bp->b_bcount); } pkt->pkt_state |= STATE_XFERRED_DATA; } aac_soft_callback(softs, acp); rval = TRAN_ACCEPT; break; case SCMD_SYNCHRONIZE_CACHE: acp->flags |= AAC_CMD_NTAG; acp->aac_cmd_fib = aac_cmd_fib_sync; acp->ac_comp = aac_synccache_complete; rval = aac_do_io(softs, acp); break; case SCMD_DOORLOCK: aac_free_dmamap(acp); dvp->locked = (pkt->pkt_cdbp[4] & 0x01) ? 1 : 0; aac_soft_callback(softs, acp); rval = TRAN_ACCEPT; break; default: /* unknown command */ aac_unknown_scmd(softs, acp); rval = TRAN_ACCEPT; break; } return (rval); } static int aac_tran_start(struct scsi_address *ap, struct scsi_pkt *pkt) { struct aac_softstate *softs = AAC_TRAN2SOFTS(ap->a_hba_tran); struct aac_cmd *acp = PKT2AC(pkt); struct aac_device *dvp = acp->dvp; int rval; DBCALLED(softs, 2); /* * Reinitialize some fields of ac and pkt; the packet may * have been resubmitted */ acp->flags &= AAC_CMD_CONSISTENT | AAC_CMD_DMA_PARTIAL | \ AAC_CMD_BUF_READ | AAC_CMD_BUF_WRITE | AAC_CMD_DMA_VALID; acp->timeout = acp->pkt->pkt_time; if (pkt->pkt_flags & FLAG_NOINTR) acp->flags |= AAC_CMD_NO_INTR; #ifdef DEBUG acp->fib_flags = AACDB_FLAGS_FIB_SCMD; #endif pkt->pkt_reason = CMD_CMPLT; pkt->pkt_state = 0; pkt->pkt_statistics = 0; *pkt->pkt_scbp = STATUS_GOOD; /* clear arq scsi_status */ if (acp->flags & AAC_CMD_DMA_VALID) { pkt->pkt_resid = acp->bcount; /* Consistent packets need to be sync'ed first */ if ((acp->flags & AAC_CMD_CONSISTENT) && (acp->flags & AAC_CMD_BUF_WRITE)) if (aac_dma_sync_ac(acp) != AACOK) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); return (TRAN_BADPKT); } } else { pkt->pkt_resid = 0; } mutex_enter(&softs->io_lock); AACDB_PRINT_SCMD(softs, acp); if ((dvp->flags & (AAC_DFLAG_VALID | AAC_DFLAG_CONFIGURING)) && !(softs->state & AAC_STATE_DEAD)) { if (dvp->type == AAC_DEV_LD) { if (ap->a_lun == 0) rval = aac_tran_start_ld(softs, acp); else goto error; } else { rval = aac_do_io(softs, acp); } } else { error: #ifdef DEBUG if (!(softs->state & AAC_STATE_DEAD)) { AACDB_PRINT_TRAN(softs, "Cannot send cmd to target t%dL%d: %s", ap->a_target, ap->a_lun, "target invalid"); } else { AACDB_PRINT(softs, CE_WARN, "Cannot send cmd to target t%dL%d: %s", ap->a_target, ap->a_lun, "adapter dead"); } #endif rval = TRAN_FATAL_ERROR; } mutex_exit(&softs->io_lock); return (rval); } static int aac_tran_getcap(struct scsi_address *ap, char *cap, int whom) { struct aac_softstate *softs = AAC_TRAN2SOFTS(ap->a_hba_tran); struct aac_device *dvp; int rval; DBCALLED(softs, 2); /* We don't allow inquiring about capabilities for other targets */ if (cap == NULL || whom == 0) { AACDB_PRINT(softs, CE_WARN, "GetCap> %s not supported: whom=%d", cap, whom); return (-1); } mutex_enter(&softs->io_lock); dvp = AAC_DEV(softs, ap->a_target); if (dvp == NULL || !AAC_DEV_IS_VALID(dvp)) { mutex_exit(&softs->io_lock); AACDB_PRINT_TRAN(softs, "Bad target t%dL%d to getcap", ap->a_target, ap->a_lun); return (-1); } switch (scsi_hba_lookup_capstr(cap)) { case SCSI_CAP_ARQ: /* auto request sense */ rval = 1; break; case SCSI_CAP_UNTAGGED_QING: case SCSI_CAP_TAGGED_QING: rval = 1; break; case SCSI_CAP_DMA_MAX: rval = softs->dma_max; break; default: rval = -1; break; } mutex_exit(&softs->io_lock); AACDB_PRINT_TRAN(softs, "GetCap> %s t%dL%d: rval=%d", cap, ap->a_target, ap->a_lun, rval); return (rval); } /*ARGSUSED*/ static int aac_tran_setcap(struct scsi_address *ap, char *cap, int value, int whom) { struct aac_softstate *softs = AAC_TRAN2SOFTS(ap->a_hba_tran); struct aac_device *dvp; int rval; DBCALLED(softs, 2); /* We don't allow inquiring about capabilities for other targets */ if (cap == NULL || whom == 0) { AACDB_PRINT(softs, CE_WARN, "SetCap> %s not supported: whom=%d", cap, whom); return (-1); } mutex_enter(&softs->io_lock); dvp = AAC_DEV(softs, ap->a_target); if (dvp == NULL || !AAC_DEV_IS_VALID(dvp)) { mutex_exit(&softs->io_lock); AACDB_PRINT_TRAN(softs, "Bad target t%dL%d to setcap", ap->a_target, ap->a_lun); return (-1); } switch (scsi_hba_lookup_capstr(cap)) { case SCSI_CAP_ARQ: /* Force auto request sense */ rval = (value == 1) ? 1 : 0; break; case SCSI_CAP_UNTAGGED_QING: case SCSI_CAP_TAGGED_QING: rval = (value == 1) ? 1 : 0; break; default: rval = -1; break; } mutex_exit(&softs->io_lock); AACDB_PRINT_TRAN(softs, "SetCap> %s t%dL%d val=%d: rval=%d", cap, ap->a_target, ap->a_lun, value, rval); return (rval); } static void aac_tran_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt) { struct aac_cmd *acp = PKT2AC(pkt); DBCALLED(NULL, 2); if (acp->sgt) { kmem_free(acp->sgt, sizeof (struct aac_sge) * \ acp->left_cookien); } aac_free_dmamap(acp); ASSERT(acp->slotp == NULL); scsi_hba_pkt_free(ap, pkt); } int aac_cmd_dma_alloc(struct aac_softstate *softs, struct aac_cmd *acp, struct buf *bp, int flags, int (*cb)(), caddr_t arg) { int kf = (cb == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP; uint_t oldcookiec; int bioerr = 0; int rval; oldcookiec = acp->left_cookien; /* Move window to build s/g map */ if (acp->total_nwin > 0) { if (++acp->cur_win < acp->total_nwin) { off_t off; size_t len; rval = ddi_dma_getwin(acp->buf_dma_handle, acp->cur_win, &off, &len, &acp->cookie, &acp->left_cookien); if (rval == DDI_SUCCESS) goto get_dma_cookies; AACDB_PRINT(softs, CE_WARN, "ddi_dma_getwin() fail %d", rval); return (AACERR); } AACDB_PRINT(softs, CE_WARN, "Nothing to transfer"); return (AACERR); } /* We need to transfer data, so we alloc DMA resources for this pkt */ if (bp && bp->b_bcount != 0 && !(acp->flags & AAC_CMD_DMA_VALID)) { uint_t dma_flags = 0; struct aac_sge *sge; /* * We will still use this point to fake some * infomation in tran_start */ acp->bp = bp; /* Set dma flags */ if (BUF_IS_READ(bp)) { dma_flags |= DDI_DMA_READ; acp->flags |= AAC_CMD_BUF_READ; } else { dma_flags |= DDI_DMA_WRITE; acp->flags |= AAC_CMD_BUF_WRITE; } if (flags & PKT_CONSISTENT) dma_flags |= DDI_DMA_CONSISTENT; if (flags & PKT_DMA_PARTIAL) dma_flags |= DDI_DMA_PARTIAL; /* Alloc buf dma handle */ if (!acp->buf_dma_handle) { rval = ddi_dma_alloc_handle(softs->devinfo_p, &softs->buf_dma_attr, cb, arg, &acp->buf_dma_handle); if (rval != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "Can't allocate DMA handle, errno=%d", rval); goto error_out; } } /* Bind buf */ if (((uintptr_t)bp->b_un.b_addr & AAC_DMA_ALIGN_MASK) == 0) { rval = ddi_dma_buf_bind_handle(acp->buf_dma_handle, bp, dma_flags, cb, arg, &acp->cookie, &acp->left_cookien); } else { size_t bufsz; AACDB_PRINT_TRAN(softs, "non-aligned buffer: addr=0x%p, cnt=%lu", (void *)bp->b_un.b_addr, bp->b_bcount); if (bp->b_flags & (B_PAGEIO|B_PHYS)) bp_mapin(bp); rval = ddi_dma_mem_alloc(acp->buf_dma_handle, AAC_ROUNDUP(bp->b_bcount, AAC_DMA_ALIGN), &softs->acc_attr, DDI_DMA_STREAMING, cb, arg, &acp->abp, &bufsz, &acp->abh); if (rval != DDI_SUCCESS) { AACDB_PRINT(softs, CE_NOTE, "Cannot alloc DMA to non-aligned buf"); bioerr = 0; goto error_out; } if (acp->flags & AAC_CMD_BUF_WRITE) ddi_rep_put8(acp->abh, (uint8_t *)bp->b_un.b_addr, (uint8_t *)acp->abp, bp->b_bcount, DDI_DEV_AUTOINCR); rval = ddi_dma_addr_bind_handle(acp->buf_dma_handle, NULL, acp->abp, bufsz, dma_flags, cb, arg, &acp->cookie, &acp->left_cookien); } switch (rval) { case DDI_DMA_PARTIAL_MAP: if (ddi_dma_numwin(acp->buf_dma_handle, &acp->total_nwin) == DDI_FAILURE) { AACDB_PRINT(softs, CE_WARN, "Cannot get number of DMA windows"); bioerr = 0; goto error_out; } AACDB_PRINT_TRAN(softs, "buf bind, %d seg(s)", acp->left_cookien); acp->cur_win = 0; break; case DDI_DMA_MAPPED: AACDB_PRINT_TRAN(softs, "buf bind, %d seg(s)", acp->left_cookien); acp->cur_win = 0; acp->total_nwin = 1; break; case DDI_DMA_NORESOURCES: bioerr = 0; AACDB_PRINT(softs, CE_WARN, "Cannot bind buf for DMA: DDI_DMA_NORESOURCES"); goto error_out; case DDI_DMA_BADATTR: case DDI_DMA_NOMAPPING: bioerr = EFAULT; AACDB_PRINT(softs, CE_WARN, "Cannot bind buf for DMA: DDI_DMA_NOMAPPING"); goto error_out; case DDI_DMA_TOOBIG: bioerr = EINVAL; AACDB_PRINT(softs, CE_WARN, "Cannot bind buf for DMA: DDI_DMA_TOOBIG(%d)", bp->b_bcount); goto error_out; default: bioerr = EINVAL; AACDB_PRINT(softs, CE_WARN, "Cannot bind buf for DMA: %d", rval); goto error_out; } acp->flags |= AAC_CMD_DMA_VALID; get_dma_cookies: ASSERT(acp->left_cookien > 0); if (acp->left_cookien > softs->aac_sg_tablesize) { AACDB_PRINT(softs, CE_NOTE, "large cookiec received %d", acp->left_cookien); bioerr = EINVAL; goto error_out; } if (oldcookiec != acp->left_cookien && acp->sgt != NULL) { kmem_free(acp->sgt, sizeof (struct aac_sge) * \ oldcookiec); acp->sgt = NULL; } if (acp->sgt == NULL) { acp->sgt = kmem_alloc(sizeof (struct aac_sge) * \ acp->left_cookien, kf); if (acp->sgt == NULL) { AACDB_PRINT(softs, CE_WARN, "sgt kmem_alloc fail"); bioerr = ENOMEM; goto error_out; } } sge = &acp->sgt[0]; sge->bcount = acp->cookie.dmac_size; sge->addr.ad64.lo = AAC_LS32(acp->cookie.dmac_laddress); sge->addr.ad64.hi = AAC_MS32(acp->cookie.dmac_laddress); acp->bcount = acp->cookie.dmac_size; for (sge++; sge < &acp->sgt[acp->left_cookien]; sge++) { ddi_dma_nextcookie(acp->buf_dma_handle, &acp->cookie); sge->bcount = acp->cookie.dmac_size; sge->addr.ad64.lo = AAC_LS32(acp->cookie.dmac_laddress); sge->addr.ad64.hi = AAC_MS32(acp->cookie.dmac_laddress); acp->bcount += acp->cookie.dmac_size; } /* * Note: The old DMA engine do not correctly handle * dma_attr_maxxfer attribute. So we have to ensure * it by ourself. */ if (acp->bcount > softs->buf_dma_attr.dma_attr_maxxfer) { AACDB_PRINT(softs, CE_NOTE, "large xfer size received %d\n", acp->bcount); bioerr = EINVAL; goto error_out; } acp->total_xfer += acp->bcount; if (acp->pkt) { /* Return remaining byte count */ if (acp->total_xfer <= bp->b_bcount) { acp->pkt->pkt_resid = bp->b_bcount - \ acp->total_xfer; } else { /* * Allocated DMA size is greater than the buf * size of bp. This is caused by devices like * tape. we have extra bytes allocated, but * the packet residual has to stay correct. */ acp->pkt->pkt_resid = 0; } AACDB_PRINT_TRAN(softs, "bp=0x%p, xfered=%d/%d, resid=%d", (void *)bp->b_un.b_addr, (int)acp->total_xfer, (int)bp->b_bcount, (int)acp->pkt->pkt_resid); } } return (AACOK); error_out: bioerror(bp, bioerr); return (AACERR); } static struct scsi_pkt * aac_tran_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt, struct buf *bp, int cmdlen, int statuslen, int tgtlen, int flags, int (*callback)(), caddr_t arg) { struct aac_softstate *softs = AAC_TRAN2SOFTS(ap->a_hba_tran); struct aac_cmd *acp, *new_acp; DBCALLED(softs, 2); /* Allocate pkt */ if (pkt == NULL) { int slen; /* Force auto request sense */ slen = (statuslen > softs->slen) ? statuslen : softs->slen; pkt = scsi_hba_pkt_alloc(softs->devinfo_p, ap, cmdlen, slen, tgtlen, sizeof (struct aac_cmd), callback, arg); if (pkt == NULL) { AACDB_PRINT(softs, CE_WARN, "Alloc scsi pkt failed"); return (NULL); } acp = new_acp = PKT2AC(pkt); acp->pkt = pkt; acp->cmdlen = cmdlen; if (ap->a_target < AAC_MAX_LD) { acp->dvp = &softs->containers[ap->a_target].dev; acp->aac_cmd_fib = softs->aac_cmd_fib; acp->ac_comp = aac_ld_complete; } else { _NOTE(ASSUMING_PROTECTED(softs->nondasds)) acp->dvp = &softs->nondasds[AAC_PD(ap->a_target)].dev; acp->aac_cmd_fib = softs->aac_cmd_fib_scsi; acp->ac_comp = aac_pd_complete; } } else { acp = PKT2AC(pkt); new_acp = NULL; } if (aac_cmd_dma_alloc(softs, acp, bp, flags, callback, arg) == AACOK) return (pkt); if (new_acp) aac_tran_destroy_pkt(ap, pkt); return (NULL); } /* * tran_sync_pkt(9E) - explicit DMA synchronization */ /*ARGSUSED*/ static void aac_tran_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt) { struct aac_cmd *acp = PKT2AC(pkt); DBCALLED(NULL, 2); if (aac_dma_sync_ac(acp) != AACOK) ddi_fm_service_impact( (AAC_TRAN2SOFTS(ap->a_hba_tran))->devinfo_p, DDI_SERVICE_UNAFFECTED); } /* * tran_dmafree(9E) - deallocate DMA resources allocated for command */ /*ARGSUSED*/ static void aac_tran_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt) { struct aac_cmd *acp = PKT2AC(pkt); DBCALLED(NULL, 2); aac_free_dmamap(acp); } static int aac_do_quiesce(struct aac_softstate *softs) { aac_hold_bus(softs, AAC_IOCMD_ASYNC); if (softs->bus_ncmds[AAC_CMDQ_ASYNC]) { aac_start_drain(softs); do { if (cv_wait_sig(&softs->drain_cv, &softs->io_lock) == 0) { /* Quiesce has been interrupted */ aac_stop_drain(softs); aac_unhold_bus(softs, AAC_IOCMD_ASYNC); aac_start_waiting_io(softs); return (AACERR); } } while (softs->bus_ncmds[AAC_CMDQ_ASYNC]); aac_stop_drain(softs); } softs->state |= AAC_STATE_QUIESCED; return (AACOK); } static int aac_tran_quiesce(dev_info_t *dip) { struct aac_softstate *softs = AAC_DIP2SOFTS(dip); int rval; DBCALLED(softs, 1); mutex_enter(&softs->io_lock); if (aac_do_quiesce(softs) == AACOK) rval = 0; else rval = 1; mutex_exit(&softs->io_lock); return (rval); } static int aac_do_unquiesce(struct aac_softstate *softs) { softs->state &= ~AAC_STATE_QUIESCED; aac_unhold_bus(softs, AAC_IOCMD_ASYNC); aac_start_waiting_io(softs); return (AACOK); } static int aac_tran_unquiesce(dev_info_t *dip) { struct aac_softstate *softs = AAC_DIP2SOFTS(dip); int rval; DBCALLED(softs, 1); mutex_enter(&softs->io_lock); if (aac_do_unquiesce(softs) == AACOK) rval = 0; else rval = 1; mutex_exit(&softs->io_lock); return (rval); } static int aac_hba_setup(struct aac_softstate *softs) { scsi_hba_tran_t *hba_tran; int rval; hba_tran = scsi_hba_tran_alloc(softs->devinfo_p, SCSI_HBA_CANSLEEP); if (hba_tran == NULL) return (AACERR); hba_tran->tran_hba_private = softs; hba_tran->tran_tgt_init = aac_tran_tgt_init; hba_tran->tran_tgt_free = aac_tran_tgt_free; hba_tran->tran_tgt_probe = scsi_hba_probe; hba_tran->tran_start = aac_tran_start; hba_tran->tran_getcap = aac_tran_getcap; hba_tran->tran_setcap = aac_tran_setcap; hba_tran->tran_init_pkt = aac_tran_init_pkt; hba_tran->tran_destroy_pkt = aac_tran_destroy_pkt; hba_tran->tran_reset = aac_tran_reset; hba_tran->tran_abort = aac_tran_abort; hba_tran->tran_sync_pkt = aac_tran_sync_pkt; hba_tran->tran_dmafree = aac_tran_dmafree; hba_tran->tran_quiesce = aac_tran_quiesce; hba_tran->tran_unquiesce = aac_tran_unquiesce; hba_tran->tran_bus_config = aac_tran_bus_config; rval = scsi_hba_attach_setup(softs->devinfo_p, &softs->buf_dma_attr, hba_tran, 0); if (rval != DDI_SUCCESS) { scsi_hba_tran_free(hba_tran); AACDB_PRINT(softs, CE_WARN, "aac_hba_setup failed"); return (AACERR); } softs->hba_tran = hba_tran; return (AACOK); } /* * FIB setup operations */ /* * Init FIB header */ static void aac_cmd_fib_header(struct aac_softstate *softs, struct aac_cmd *acp, uint16_t cmd) { struct aac_slot *slotp = acp->slotp; ddi_acc_handle_t acc = slotp->fib_acc_handle; struct aac_fib *fibp = slotp->fibp; uint32_t xfer_state; xfer_state = AAC_FIBSTATE_HOSTOWNED | AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_EMPTY | AAC_FIBSTATE_FAST_RESPONSE | /* enable fast io */ AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_REXPECTED | AAC_FIBSTATE_NORM; if (!(acp->flags & AAC_CMD_SYNC)) xfer_state |= AAC_FIBSTATE_ASYNC; ddi_put32(acc, &fibp->Header.XferState, xfer_state); ddi_put16(acc, &fibp->Header.Command, cmd); ddi_put8(acc, &fibp->Header.StructType, AAC_FIBTYPE_TFIB); ddi_put8(acc, &fibp->Header.Flags, 0); /* don't care */ ddi_put16(acc, &fibp->Header.Size, acp->fib_size); ddi_put16(acc, &fibp->Header.SenderSize, softs->aac_max_fib_size); ddi_put32(acc, &fibp->Header.SenderFibAddress, (slotp->index << 2)); ddi_put32(acc, &fibp->Header.ReceiverFibAddress, slotp->fib_phyaddr); ddi_put32(acc, &fibp->Header.SenderData, 0); /* don't care */ } /* * Init FIB for raw IO command */ static void aac_cmd_fib_rawio(struct aac_softstate *softs, struct aac_cmd *acp) { ddi_acc_handle_t acc = acp->slotp->fib_acc_handle; struct aac_raw_io *io = (struct aac_raw_io *)&acp->slotp->fibp->data[0]; struct aac_sg_entryraw *sgp; struct aac_sge *sge; /* Calculate FIB size */ acp->fib_size = sizeof (struct aac_fib_header) + \ sizeof (struct aac_raw_io) + (acp->left_cookien - 1) * \ sizeof (struct aac_sg_entryraw); aac_cmd_fib_header(softs, acp, RawIo); ddi_put16(acc, &io->Flags, (acp->flags & AAC_CMD_BUF_READ) ? 1 : 0); ddi_put16(acc, &io->BpTotal, 0); ddi_put16(acc, &io->BpComplete, 0); ddi_put32(acc, AAC_LO32(&io->BlockNumber), AAC_LS32(acp->blkno)); ddi_put32(acc, AAC_HI32(&io->BlockNumber), AAC_MS32(acp->blkno)); ddi_put16(acc, &io->ContainerId, ((struct aac_container *)acp->dvp)->cid); /* Fill SG table */ ddi_put32(acc, &io->SgMapRaw.SgCount, acp->left_cookien); ddi_put32(acc, &io->ByteCount, acp->bcount); for (sge = &acp->sgt[0], sgp = &io->SgMapRaw.SgEntryRaw[0]; sge < &acp->sgt[acp->left_cookien]; sge++, sgp++) { ddi_put32(acc, AAC_LO32(&sgp->SgAddress), sge->addr.ad64.lo); ddi_put32(acc, AAC_HI32(&sgp->SgAddress), sge->addr.ad64.hi); ddi_put32(acc, &sgp->SgByteCount, sge->bcount); sgp->Next = 0; sgp->Prev = 0; sgp->Flags = 0; } } /* Init FIB for 64-bit block IO command */ static void aac_cmd_fib_brw64(struct aac_softstate *softs, struct aac_cmd *acp) { ddi_acc_handle_t acc = acp->slotp->fib_acc_handle; struct aac_blockread64 *br = (struct aac_blockread64 *) \ &acp->slotp->fibp->data[0]; struct aac_sg_entry64 *sgp; struct aac_sge *sge; acp->fib_size = sizeof (struct aac_fib_header) + \ sizeof (struct aac_blockread64) + (acp->left_cookien - 1) * \ sizeof (struct aac_sg_entry64); aac_cmd_fib_header(softs, acp, ContainerCommand64); /* * The definitions for aac_blockread64 and aac_blockwrite64 * are the same. */ ddi_put32(acc, &br->BlockNumber, (uint32_t)acp->blkno); ddi_put16(acc, &br->ContainerId, ((struct aac_container *)acp->dvp)->cid); ddi_put32(acc, &br->Command, (acp->flags & AAC_CMD_BUF_READ) ? VM_CtHostRead64 : VM_CtHostWrite64); ddi_put16(acc, &br->Pad, 0); ddi_put16(acc, &br->Flags, 0); /* Fill SG table */ ddi_put32(acc, &br->SgMap64.SgCount, acp->left_cookien); ddi_put16(acc, &br->SectorCount, acp->bcount / AAC_BLK_SIZE); for (sge = &acp->sgt[0], sgp = &br->SgMap64.SgEntry64[0]; sge < &acp->sgt[acp->left_cookien]; sge++, sgp++) { ddi_put32(acc, AAC_LO32(&sgp->SgAddress), sge->addr.ad64.lo); ddi_put32(acc, AAC_HI32(&sgp->SgAddress), sge->addr.ad64.hi); ddi_put32(acc, &sgp->SgByteCount, sge->bcount); } } /* Init FIB for block IO command */ static void aac_cmd_fib_brw(struct aac_softstate *softs, struct aac_cmd *acp) { ddi_acc_handle_t acc = acp->slotp->fib_acc_handle; struct aac_blockread *br = (struct aac_blockread *) \ &acp->slotp->fibp->data[0]; struct aac_sg_entry *sgp; struct aac_sge *sge = &acp->sgt[0]; if (acp->flags & AAC_CMD_BUF_READ) { acp->fib_size = sizeof (struct aac_fib_header) + \ sizeof (struct aac_blockread) + (acp->left_cookien - 1) * \ sizeof (struct aac_sg_entry); ddi_put32(acc, &br->Command, VM_CtBlockRead); ddi_put32(acc, &br->SgMap.SgCount, acp->left_cookien); sgp = &br->SgMap.SgEntry[0]; } else { struct aac_blockwrite *bw = (struct aac_blockwrite *)br; acp->fib_size = sizeof (struct aac_fib_header) + \ sizeof (struct aac_blockwrite) + (acp->left_cookien - 1) * \ sizeof (struct aac_sg_entry); ddi_put32(acc, &bw->Command, VM_CtBlockWrite); ddi_put32(acc, &bw->Stable, CUNSTABLE); ddi_put32(acc, &bw->SgMap.SgCount, acp->left_cookien); sgp = &bw->SgMap.SgEntry[0]; } aac_cmd_fib_header(softs, acp, ContainerCommand); /* * aac_blockread and aac_blockwrite have the similar * structure head, so use br for bw here */ ddi_put32(acc, &br->BlockNumber, (uint32_t)acp->blkno); ddi_put32(acc, &br->ContainerId, ((struct aac_container *)acp->dvp)->cid); ddi_put32(acc, &br->ByteCount, acp->bcount); /* Fill SG table */ for (sge = &acp->sgt[0]; sge < &acp->sgt[acp->left_cookien]; sge++, sgp++) { ddi_put32(acc, &sgp->SgAddress, sge->addr.ad32); ddi_put32(acc, &sgp->SgByteCount, sge->bcount); } } /*ARGSUSED*/ void aac_cmd_fib_copy(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_slot *slotp = acp->slotp; struct aac_fib *fibp = slotp->fibp; ddi_acc_handle_t acc = slotp->fib_acc_handle; ddi_rep_put8(acc, (uint8_t *)acp->fibp, (uint8_t *)fibp, acp->fib_size, /* only copy data of needed length */ DDI_DEV_AUTOINCR); ddi_put32(acc, &fibp->Header.ReceiverFibAddress, slotp->fib_phyaddr); ddi_put32(acc, &fibp->Header.SenderFibAddress, slotp->index << 2); } static void aac_cmd_fib_sync(struct aac_softstate *softs, struct aac_cmd *acp) { ddi_acc_handle_t acc = acp->slotp->fib_acc_handle; struct aac_synchronize_command *sync = (struct aac_synchronize_command *)&acp->slotp->fibp->data[0]; acp->fib_size = AAC_FIB_SIZEOF(struct aac_synchronize_command); aac_cmd_fib_header(softs, acp, ContainerCommand); ddi_put32(acc, &sync->Command, VM_ContainerConfig); ddi_put32(acc, &sync->Type, (uint32_t)CT_FLUSH_CACHE); ddi_put32(acc, &sync->Cid, ((struct aac_container *)acp->dvp)->cid); ddi_put32(acc, &sync->Count, sizeof (((struct aac_synchronize_reply *)0)->Data)); } /* * Start/Stop unit (Power Management) */ static void aac_cmd_fib_startstop(struct aac_softstate *softs, struct aac_cmd *acp) { ddi_acc_handle_t acc = acp->slotp->fib_acc_handle; struct aac_Container *cmd = (struct aac_Container *)&acp->slotp->fibp->data[0]; union scsi_cdb *cdbp = (void *)acp->pkt->pkt_cdbp; acp->fib_size = AAC_FIB_SIZEOF(struct aac_Container); aac_cmd_fib_header(softs, acp, ContainerCommand); bzero(cmd, sizeof (*cmd) - CT_PACKET_SIZE); ddi_put32(acc, &cmd->Command, VM_ContainerConfig); ddi_put32(acc, &cmd->CTCommand.command, CT_PM_DRIVER_SUPPORT); ddi_put32(acc, &cmd->CTCommand.param[0], cdbp->cdb_opaque[4] & 1 ? \ AAC_PM_DRIVERSUP_START_UNIT : AAC_PM_DRIVERSUP_STOP_UNIT); ddi_put32(acc, &cmd->CTCommand.param[1], ((struct aac_container *)acp->dvp)->cid); ddi_put32(acc, &cmd->CTCommand.param[2], cdbp->cdb_opaque[1] & 1); } /* * Init FIB for pass-through SCMD */ static void aac_cmd_fib_srb(struct aac_cmd *acp) { ddi_acc_handle_t acc = acp->slotp->fib_acc_handle; struct aac_srb *srb = (struct aac_srb *)&acp->slotp->fibp->data[0]; uint8_t *cdb; ddi_put32(acc, &srb->function, SRBF_ExecuteScsi); ddi_put32(acc, &srb->retry_limit, 0); ddi_put32(acc, &srb->cdb_size, acp->cmdlen); ddi_put32(acc, &srb->timeout, 0); /* use driver timeout */ if (acp->fibp == NULL) { if (acp->flags & AAC_CMD_BUF_READ) ddi_put32(acc, &srb->flags, SRB_DataIn); else if (acp->flags & AAC_CMD_BUF_WRITE) ddi_put32(acc, &srb->flags, SRB_DataOut); ddi_put32(acc, &srb->channel, ((struct aac_nondasd *)acp->dvp)->bus); ddi_put32(acc, &srb->id, ((struct aac_nondasd *)acp->dvp)->tid); ddi_put32(acc, &srb->lun, 0); cdb = acp->pkt->pkt_cdbp; } else { struct aac_srb *srb0 = (struct aac_srb *)&acp->fibp->data[0]; ddi_put32(acc, &srb->flags, srb0->flags); ddi_put32(acc, &srb->channel, srb0->channel); ddi_put32(acc, &srb->id, srb0->id); ddi_put32(acc, &srb->lun, srb0->lun); cdb = srb0->cdb; } ddi_rep_put8(acc, cdb, srb->cdb, acp->cmdlen, DDI_DEV_AUTOINCR); } static void aac_cmd_fib_scsi32(struct aac_softstate *softs, struct aac_cmd *acp) { ddi_acc_handle_t acc = acp->slotp->fib_acc_handle; struct aac_srb *srb = (struct aac_srb *)&acp->slotp->fibp->data[0]; struct aac_sg_entry *sgp; struct aac_sge *sge; acp->fib_size = sizeof (struct aac_fib_header) + \ sizeof (struct aac_srb) - sizeof (struct aac_sg_entry) + \ acp->left_cookien * sizeof (struct aac_sg_entry); /* Fill FIB and SRB headers, and copy cdb */ aac_cmd_fib_header(softs, acp, ScsiPortCommand); aac_cmd_fib_srb(acp); /* Fill SG table */ ddi_put32(acc, &srb->sg.SgCount, acp->left_cookien); ddi_put32(acc, &srb->count, acp->bcount); for (sge = &acp->sgt[0], sgp = &srb->sg.SgEntry[0]; sge < &acp->sgt[acp->left_cookien]; sge++, sgp++) { ddi_put32(acc, &sgp->SgAddress, sge->addr.ad32); ddi_put32(acc, &sgp->SgByteCount, sge->bcount); } } static void aac_cmd_fib_scsi64(struct aac_softstate *softs, struct aac_cmd *acp) { ddi_acc_handle_t acc = acp->slotp->fib_acc_handle; struct aac_srb *srb = (struct aac_srb *)&acp->slotp->fibp->data[0]; struct aac_sg_entry64 *sgp; struct aac_sge *sge; acp->fib_size = sizeof (struct aac_fib_header) + \ sizeof (struct aac_srb) - sizeof (struct aac_sg_entry) + \ acp->left_cookien * sizeof (struct aac_sg_entry64); /* Fill FIB and SRB headers, and copy cdb */ aac_cmd_fib_header(softs, acp, ScsiPortCommandU64); aac_cmd_fib_srb(acp); /* Fill SG table */ ddi_put32(acc, &srb->sg.SgCount, acp->left_cookien); ddi_put32(acc, &srb->count, acp->bcount); for (sge = &acp->sgt[0], sgp = &((struct aac_sg_table64 *)&srb->sg)->SgEntry64[0]; sge < &acp->sgt[acp->left_cookien]; sge++, sgp++) { ddi_put32(acc, AAC_LO32(&sgp->SgAddress), sge->addr.ad64.lo); ddi_put32(acc, AAC_HI32(&sgp->SgAddress), sge->addr.ad64.hi); ddi_put32(acc, &sgp->SgByteCount, sge->bcount); } } static int aac_cmd_slot_bind(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_slot *slotp; if (slotp = aac_get_slot(softs)) { acp->slotp = slotp; slotp->acp = acp; acp->aac_cmd_fib(softs, acp); (void) ddi_dma_sync(slotp->fib_dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); return (AACOK); } return (AACERR); } static int aac_bind_io(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_device *dvp = acp->dvp; int q = AAC_CMDQ(acp); if (softs->bus_ncmds[q] < softs->bus_throttle[q]) { if (dvp) { if (dvp->ncmds[q] < dvp->throttle[q]) { if (!(acp->flags & AAC_CMD_NTAG) || dvp->ncmds[q] == 0) { return (aac_cmd_slot_bind(softs, acp)); } ASSERT(q == AAC_CMDQ_ASYNC); aac_set_throttle(softs, dvp, AAC_CMDQ_ASYNC, AAC_THROTTLE_DRAIN); } } else { return (aac_cmd_slot_bind(softs, acp)); } } return (AACERR); } static int aac_sync_fib_slot_bind(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_slot *slotp; while (softs->sync_ac.slotp) cv_wait(&softs->sync_fib_cv, &softs->io_lock); if (slotp = aac_get_slot(softs)) { ASSERT(acp->slotp == NULL); acp->slotp = slotp; slotp->acp = acp; return (AACOK); } return (AACERR); } static void aac_sync_fib_slot_release(struct aac_softstate *softs, struct aac_cmd *acp) { ASSERT(acp->slotp); aac_release_slot(softs, acp->slotp); acp->slotp->acp = NULL; acp->slotp = NULL; cv_signal(&softs->sync_fib_cv); } static void aac_start_io(struct aac_softstate *softs, struct aac_cmd *acp) { struct aac_slot *slotp = acp->slotp; int q = AAC_CMDQ(acp); int rval; /* Set ac and pkt */ if (acp->pkt) { /* ac from ioctl has no pkt */ acp->pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD; } if (acp->timeout) /* 0 indicates no timeout */ acp->timeout += aac_timebase + aac_tick; if (acp->dvp) acp->dvp->ncmds[q]++; softs->bus_ncmds[q]++; aac_cmd_enqueue(&softs->q_busy, acp); AACDB_PRINT_FIB(softs, slotp); if (softs->flags & AAC_FLAGS_NEW_COMM) { rval = aac_send_command(softs, slotp); } else { /* * If fib can not be enqueued, the adapter is in an abnormal * state, there will be no interrupt to us. */ rval = aac_fib_enqueue(softs, AAC_ADAP_NORM_CMD_Q, slotp->fib_phyaddr, acp->fib_size); } if (aac_check_dma_handle(slotp->fib_dma_handle) != DDI_SUCCESS) ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); /* * NOTE: We send command only when slots availabe, so should never * reach here. */ if (rval != AACOK) { AACDB_PRINT(softs, CE_NOTE, "SCMD send failed"); if (acp->pkt) { acp->pkt->pkt_state &= ~STATE_SENT_CMD; aac_set_pkt_reason(softs, acp, CMD_INCOMPLETE, 0); } aac_end_io(softs, acp); if (!(acp->flags & (AAC_CMD_NO_INTR | AAC_CMD_NO_CB))) ddi_trigger_softintr(softs->softint_id); } } static void aac_start_waitq(struct aac_softstate *softs, struct aac_cmd_queue *q) { struct aac_cmd *acp, *next_acp; /* Serve as many waiting io's as possible */ for (acp = q->q_head; acp; acp = next_acp) { next_acp = acp->next; if (aac_bind_io(softs, acp) == AACOK) { aac_cmd_delete(q, acp); aac_start_io(softs, acp); } if (softs->free_io_slot_head == NULL) break; } } static void aac_start_waiting_io(struct aac_softstate *softs) { /* * Sync FIB io is served before async FIB io so that io requests * sent by interactive userland commands get responded asap. */ if (softs->q_wait[AAC_CMDQ_SYNC].q_head) aac_start_waitq(softs, &softs->q_wait[AAC_CMDQ_SYNC]); if (softs->q_wait[AAC_CMDQ_ASYNC].q_head) aac_start_waitq(softs, &softs->q_wait[AAC_CMDQ_ASYNC]); } static void aac_drain_comp_q(struct aac_softstate *softs) { struct aac_cmd *acp; struct scsi_pkt *pkt; /*CONSTCOND*/ while (1) { mutex_enter(&softs->q_comp_mutex); acp = aac_cmd_dequeue(&softs->q_comp); mutex_exit(&softs->q_comp_mutex); if (acp != NULL) { ASSERT(acp->pkt != NULL); pkt = acp->pkt; if (pkt->pkt_reason == CMD_CMPLT) { /* * Consistent packets need to be sync'ed first */ if ((acp->flags & AAC_CMD_CONSISTENT) && (acp->flags & AAC_CMD_BUF_READ)) { if (aac_dma_sync_ac(acp) != AACOK) { ddi_fm_service_impact( softs->devinfo_p, DDI_SERVICE_UNAFFECTED); pkt->pkt_reason = CMD_TRAN_ERR; pkt->pkt_statistics = 0; } } if ((aac_check_acc_handle(softs-> \ comm_space_acc_handle) != DDI_SUCCESS) || (aac_check_acc_handle(softs-> \ pci_mem_handle) != DDI_SUCCESS)) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); ddi_fm_acc_err_clear(softs-> \ pci_mem_handle, DDI_FME_VER0); pkt->pkt_reason = CMD_TRAN_ERR; pkt->pkt_statistics = 0; } if (aac_check_dma_handle(softs-> \ comm_space_dma_handle) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); pkt->pkt_reason = CMD_TRAN_ERR; pkt->pkt_statistics = 0; } } scsi_hba_pkt_comp(pkt); } else { break; } } } static int aac_alloc_fib(struct aac_softstate *softs, struct aac_slot *slotp) { size_t rlen; ddi_dma_cookie_t cookie; uint_t cookien; /* Allocate FIB dma resource */ if (ddi_dma_alloc_handle( softs->devinfo_p, &softs->addr_dma_attr, DDI_DMA_SLEEP, NULL, &slotp->fib_dma_handle) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "Cannot alloc dma handle for slot fib area"); goto error; } if (ddi_dma_mem_alloc( slotp->fib_dma_handle, softs->aac_max_fib_size, &softs->acc_attr, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, (caddr_t *)&slotp->fibp, &rlen, &slotp->fib_acc_handle) != DDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "Cannot alloc mem for slot fib area"); goto error; } if (ddi_dma_addr_bind_handle( slotp->fib_dma_handle, NULL, (caddr_t)slotp->fibp, softs->aac_max_fib_size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &cookien) != DDI_DMA_MAPPED) { AACDB_PRINT(softs, CE_WARN, "dma bind failed for slot fib area"); goto error; } /* Check dma handles allocated in fib attach */ if (aac_check_dma_handle(slotp->fib_dma_handle) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); goto error; } /* Check acc handles allocated in fib attach */ if (aac_check_acc_handle(slotp->fib_acc_handle) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_LOST); goto error; } slotp->fib_phyaddr = cookie.dmac_laddress; return (AACOK); error: if (slotp->fib_acc_handle) { ddi_dma_mem_free(&slotp->fib_acc_handle); slotp->fib_acc_handle = NULL; } if (slotp->fib_dma_handle) { ddi_dma_free_handle(&slotp->fib_dma_handle); slotp->fib_dma_handle = NULL; } return (AACERR); } static void aac_free_fib(struct aac_slot *slotp) { (void) ddi_dma_unbind_handle(slotp->fib_dma_handle); ddi_dma_mem_free(&slotp->fib_acc_handle); slotp->fib_acc_handle = NULL; ddi_dma_free_handle(&slotp->fib_dma_handle); slotp->fib_dma_handle = NULL; slotp->fib_phyaddr = 0; } static void aac_alloc_fibs(struct aac_softstate *softs) { int i; struct aac_slot *slotp; for (i = 0; i < softs->total_slots && softs->total_fibs < softs->total_slots; i++) { slotp = &(softs->io_slot[i]); if (slotp->fib_phyaddr) continue; if (aac_alloc_fib(softs, slotp) != AACOK) break; /* Insert the slot to the free slot list */ aac_release_slot(softs, slotp); softs->total_fibs++; } } static void aac_destroy_fibs(struct aac_softstate *softs) { struct aac_slot *slotp; while ((slotp = softs->free_io_slot_head) != NULL) { ASSERT(slotp->fib_phyaddr); softs->free_io_slot_head = slotp->next; aac_free_fib(slotp); ASSERT(slotp->index == (slotp - softs->io_slot)); softs->total_fibs--; } ASSERT(softs->total_fibs == 0); } static int aac_create_slots(struct aac_softstate *softs) { int i; softs->total_slots = softs->aac_max_fibs; softs->io_slot = kmem_zalloc(sizeof (struct aac_slot) * \ softs->total_slots, KM_SLEEP); if (softs->io_slot == NULL) { AACDB_PRINT(softs, CE_WARN, "Cannot allocate slot"); return (AACERR); } for (i = 0; i < softs->total_slots; i++) softs->io_slot[i].index = i; softs->free_io_slot_head = NULL; softs->total_fibs = 0; return (AACOK); } static void aac_destroy_slots(struct aac_softstate *softs) { ASSERT(softs->free_io_slot_head == NULL); kmem_free(softs->io_slot, sizeof (struct aac_slot) * \ softs->total_slots); softs->io_slot = NULL; softs->total_slots = 0; } struct aac_slot * aac_get_slot(struct aac_softstate *softs) { struct aac_slot *slotp; if ((slotp = softs->free_io_slot_head) != NULL) { softs->free_io_slot_head = slotp->next; slotp->next = NULL; } return (slotp); } static void aac_release_slot(struct aac_softstate *softs, struct aac_slot *slotp) { ASSERT((slotp->index >= 0) && (slotp->index < softs->total_slots)); ASSERT(slotp == &softs->io_slot[slotp->index]); slotp->acp = NULL; slotp->next = softs->free_io_slot_head; softs->free_io_slot_head = slotp; } int aac_do_io(struct aac_softstate *softs, struct aac_cmd *acp) { if (aac_bind_io(softs, acp) == AACOK) aac_start_io(softs, acp); else aac_cmd_enqueue(&softs->q_wait[AAC_CMDQ(acp)], acp); if (!(acp->flags & (AAC_CMD_NO_CB | AAC_CMD_NO_INTR))) return (TRAN_ACCEPT); /* * Because sync FIB is always 512 bytes and used for critical * functions, async FIB is used for poll IO. */ if (acp->flags & AAC_CMD_NO_INTR) { if (aac_do_poll_io(softs, acp) == AACOK) return (TRAN_ACCEPT); } else { if (aac_do_sync_io(softs, acp) == AACOK) return (TRAN_ACCEPT); } return (TRAN_BADPKT); } static int aac_do_poll_io(struct aac_softstate *softs, struct aac_cmd *acp) { int (*intr_handler)(struct aac_softstate *); /* * Interrupt is disabled, we have to poll the adapter by ourselves. */ intr_handler = (softs->flags & AAC_FLAGS_NEW_COMM) ? aac_process_intr_new : aac_process_intr_old; while (!(acp->flags & (AAC_CMD_CMPLT | AAC_CMD_ABORT))) { int i = AAC_POLL_TIME * 1000; AAC_BUSYWAIT((intr_handler(softs) != AAC_DB_RESPONSE_READY), i); if (i == 0) aac_cmd_timeout(softs, acp); } ddi_trigger_softintr(softs->softint_id); if ((acp->flags & AAC_CMD_CMPLT) && !(acp->flags & AAC_CMD_ERR)) return (AACOK); return (AACERR); } static int aac_do_sync_io(struct aac_softstate *softs, struct aac_cmd *acp) { ASSERT(softs && acp); while (!(acp->flags & (AAC_CMD_CMPLT | AAC_CMD_ABORT))) cv_wait(&softs->event, &softs->io_lock); if (acp->flags & AAC_CMD_CMPLT) return (AACOK); return (AACERR); } static int aac_dma_sync_ac(struct aac_cmd *acp) { if (acp->buf_dma_handle) { if (acp->flags & AAC_CMD_BUF_WRITE) { if (acp->abp != NULL) ddi_rep_put8(acp->abh, (uint8_t *)acp->bp->b_un.b_addr, (uint8_t *)acp->abp, acp->bp->b_bcount, DDI_DEV_AUTOINCR); (void) ddi_dma_sync(acp->buf_dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); } else { (void) ddi_dma_sync(acp->buf_dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU); if (aac_check_dma_handle(acp->buf_dma_handle) != DDI_SUCCESS) return (AACERR); if (acp->abp != NULL) ddi_rep_get8(acp->abh, (uint8_t *)acp->bp->b_un.b_addr, (uint8_t *)acp->abp, acp->bp->b_bcount, DDI_DEV_AUTOINCR); } } return (AACOK); } /* * Copy AIF from adapter to the empty AIF slot and inform AIF threads */ static void aac_save_aif(struct aac_softstate *softs, ddi_acc_handle_t acc, struct aac_fib *fibp0, int fib_size0) { struct aac_fib *fibp; /* FIB in AIF queue */ int fib_size; uint16_t fib_command; int current, next; /* Ignore non AIF messages */ fib_command = ddi_get16(acc, &fibp0->Header.Command); if (fib_command != AifRequest) { cmn_err(CE_WARN, "!Unknown command from controller"); return; } mutex_enter(&softs->aifq_mutex); /* Save AIF */ fibp = &softs->aifq[softs->aifq_idx].d; fib_size = (fib_size0 > AAC_FIB_SIZE) ? AAC_FIB_SIZE : fib_size0; ddi_rep_get8(acc, (uint8_t *)fibp, (uint8_t *)fibp0, fib_size, DDI_DEV_AUTOINCR); if (aac_check_acc_handle(softs->pci_mem_handle) != DDI_SUCCESS) { ddi_fm_service_impact(softs->devinfo_p, DDI_SERVICE_UNAFFECTED); mutex_exit(&softs->aifq_mutex); return; } AACDB_PRINT_AIF(softs, (struct aac_aif_command *)&fibp->data[0]); /* Modify AIF contexts */ current = softs->aifq_idx; next = (current + 1) % AAC_AIFQ_LENGTH; if (next == 0) { struct aac_fib_context *ctx_p; softs->aifq_wrap = 1; for (ctx_p = softs->fibctx_p; ctx_p; ctx_p = ctx_p->next) { if (next == ctx_p->ctx_idx) { ctx_p->ctx_flags |= AAC_CTXFLAG_FILLED; } else if (current == ctx_p->ctx_idx && (ctx_p->ctx_flags & AAC_CTXFLAG_FILLED)) { ctx_p->ctx_idx = next; ctx_p->ctx_overrun++; } } } softs->aifq_idx = next; /* Wakeup AIF threads */ cv_broadcast(&softs->aifq_cv); mutex_exit(&softs->aifq_mutex); /* Wakeup event thread to handle aif */ aac_event_disp(softs, AAC_EVENT_AIF); } static int aac_return_aif_common(struct aac_softstate *softs, struct aac_fib_context *ctx, struct aac_fib **fibpp) { int current; current = ctx->ctx_idx; if (current == softs->aifq_idx && !(ctx->ctx_flags & AAC_CTXFLAG_FILLED)) return (EAGAIN); /* Empty */ *fibpp = &softs->aifq[current].d; ctx->ctx_flags &= ~AAC_CTXFLAG_FILLED; ctx->ctx_idx = (current + 1) % AAC_AIFQ_LENGTH; return (0); } int aac_return_aif(struct aac_softstate *softs, struct aac_fib_context *ctx, struct aac_fib **fibpp) { int rval; mutex_enter(&softs->aifq_mutex); rval = aac_return_aif_common(softs, ctx, fibpp); mutex_exit(&softs->aifq_mutex); return (rval); } int aac_return_aif_wait(struct aac_softstate *softs, struct aac_fib_context *ctx, struct aac_fib **fibpp) { int rval; mutex_enter(&softs->aifq_mutex); rval = aac_return_aif_common(softs, ctx, fibpp); if (rval == EAGAIN) { AACDB_PRINT(softs, CE_NOTE, "Waiting for AIF"); rval = cv_wait_sig(&softs->aifq_cv, &softs->aifq_mutex); } mutex_exit(&softs->aifq_mutex); return ((rval > 0) ? 0 : EINTR); } /* * The following function comes from Adaptec: * * When driver sees a particular event that means containers are changed, it * will rescan containers. However a change may not be complete until some * other event is received. For example, creating or deleting an array will * incur as many as six AifEnConfigChange events which would generate six * container rescans. To diminish rescans, driver set a flag to wait for * another particular event. When sees that events come in, it will do rescan. */ static int aac_handle_aif(struct aac_softstate *softs, struct aac_aif_command *aif) { ddi_acc_handle_t acc = softs->comm_space_acc_handle; int en_type; int devcfg_needed; int cid; uint32_t bus_id, tgt_id; enum aac_cfg_event event = AAC_CFG_NULL_EXIST; devcfg_needed = 0; en_type = LE_32((uint32_t)aif->data.EN.type); switch (LE_32((uint32_t)aif->command)) { case AifCmdDriverNotify: { cid = LE_32(aif->data.EN.data.ECC.container[0]); switch (en_type) { case AifDenMorphComplete: case AifDenVolumeExtendComplete: if (AAC_DEV_IS_VALID(&softs->containers[cid].dev)) softs->devcfg_wait_on = AifEnConfigChange; break; } if (softs->devcfg_wait_on == en_type) devcfg_needed = 1; break; } case AifCmdEventNotify: cid = LE_32(aif->data.EN.data.ECC.container[0]); switch (en_type) { case AifEnAddContainer: case AifEnDeleteContainer: softs->devcfg_wait_on = AifEnConfigChange; break; case AifEnContainerChange: if (!softs->devcfg_wait_on) softs->devcfg_wait_on = AifEnConfigChange; break; case AifEnContainerEvent: if (ddi_get32(acc, &aif-> \ data.EN.data.ECE.eventType) == CT_PUP_MISSING_DRIVE) devcfg_needed = 1; break; case AifEnAddJBOD: if (!(softs->flags & AAC_FLAGS_JBOD)) return (AACERR); event = AAC_CFG_ADD; bus_id = (cid >> 24) & 0xf; tgt_id = cid & 0xffff; break; case AifEnDeleteJBOD: if (!(softs->flags & AAC_FLAGS_JBOD)) return (AACERR); event = AAC_CFG_DELETE; bus_id = (cid >> 24) & 0xf; tgt_id = cid & 0xffff; break; } if (softs->devcfg_wait_on == en_type) devcfg_needed = 1; break; case AifCmdJobProgress: if (LE_32((uint32_t)aif->data.PR[0].jd.type) == AifJobCtrZero) { int pr_status; uint32_t pr_ftick, pr_ctick; pr_status = LE_32((uint32_t)aif->data.PR[0].status); pr_ctick = LE_32(aif->data.PR[0].currentTick); pr_ftick = LE_32(aif->data.PR[0].finalTick); if ((pr_ctick == pr_ftick) || (pr_status == AifJobStsSuccess)) softs->devcfg_wait_on = AifEnContainerChange; else if ((pr_ctick == 0) && (pr_status == AifJobStsRunning)) softs->devcfg_wait_on = AifEnContainerChange; } break; } if (devcfg_needed) { softs->devcfg_wait_on = 0; (void) aac_probe_containers(softs); } if (event != AAC_CFG_NULL_EXIST) { ASSERT(en_type == AifEnAddJBOD || en_type == AifEnDeleteJBOD); (void) aac_probe_jbod(softs, AAC_P2VTGT(softs, bus_id, tgt_id), event); } return (AACOK); } /* * Check and handle AIF events */ static void aac_aif_event(struct aac_softstate *softs) { struct aac_fib *fibp; /*CONSTCOND*/ while (1) { if (aac_return_aif(softs, &softs->aifctx, &fibp) != 0) break; /* No more AIFs to handle, end loop */ /* AIF overrun, array create/delete may missed. */ if (softs->aifctx.ctx_overrun) { softs->aifctx.ctx_overrun = 0; } /* AIF received, handle it */ struct aac_aif_command *aifp = (struct aac_aif_command *)&fibp->data[0]; uint32_t aif_command = LE_32((uint32_t)aifp->command); if (aif_command == AifCmdDriverNotify || aif_command == AifCmdEventNotify || aif_command == AifCmdJobProgress) (void) aac_handle_aif(softs, aifp); } } /* * Timeout recovery */ /*ARGSUSED*/ static void aac_cmd_timeout(struct aac_softstate *softs, struct aac_cmd *acp) { #ifdef DEBUG acp->fib_flags |= AACDB_FLAGS_FIB_TIMEOUT; AACDB_PRINT(softs, CE_WARN, "acp %p timed out", acp); AACDB_PRINT_FIB(softs, acp->slotp); #endif /* * Besides the firmware in unhealthy state, an overloaded * adapter may also incur pkt timeout. * There is a chance for an adapter with a slower IOP to take * longer than 60 seconds to process the commands, such as when * to perform IOs. So the adapter is doing a build on a RAID-5 * while being required longer completion times should be * tolerated. */ switch (aac_do_reset(softs)) { case AAC_IOP_RESET_SUCCEED: aac_abort_iocmds(softs, AAC_IOCMD_OUTSTANDING, NULL, CMD_RESET); aac_start_waiting_io(softs); break; case AAC_IOP_RESET_FAILED: /* Abort all waiting cmds when adapter is dead */ aac_abort_iocmds(softs, AAC_IOCMD_ALL, NULL, CMD_TIMEOUT); break; case AAC_IOP_RESET_ABNORMAL: aac_start_waiting_io(softs); } } /* * The following function comes from Adaptec: * * Time sync. command added to synchronize time with firmware every 30 * minutes (required for correct AIF timestamps etc.) */ static void aac_sync_tick(struct aac_softstate *softs) { ddi_acc_handle_t acc; int rval; mutex_enter(&softs->time_mutex); ASSERT(softs->time_sync <= softs->timebase); softs->time_sync = 0; mutex_exit(&softs->time_mutex); /* Time sync. with firmware every AAC_SYNC_TICK */ (void) aac_sync_fib_slot_bind(softs, &softs->sync_ac); acc = softs->sync_ac.slotp->fib_acc_handle; ddi_put32(acc, (void *)&softs->sync_ac.slotp->fibp->data[0], ddi_get_time()); rval = aac_sync_fib(softs, SendHostTime, AAC_FIB_SIZEOF(uint32_t)); aac_sync_fib_slot_release(softs, &softs->sync_ac); mutex_enter(&softs->time_mutex); softs->time_sync = softs->timebase; if (rval != AACOK) /* retry shortly */ softs->time_sync += aac_tick << 1; else softs->time_sync += AAC_SYNC_TICK; mutex_exit(&softs->time_mutex); } /* * Timeout checking and handling */ static void aac_daemon(struct aac_softstate *softs) { int time_out; /* set if timeout happened */ int time_adjust; uint32_t softs_timebase; mutex_enter(&softs->time_mutex); ASSERT(softs->time_out <= softs->timebase); softs->time_out = 0; softs_timebase = softs->timebase; mutex_exit(&softs->time_mutex); /* Check slots for timeout pkts */ time_adjust = 0; do { struct aac_cmd *acp; time_out = 0; for (acp = softs->q_busy.q_head; acp; acp = acp->next) { if (acp->timeout == 0) continue; /* * If timeout happened, update outstanding cmds * to be checked later again. */ if (time_adjust) { acp->timeout += time_adjust; continue; } if (acp->timeout <= softs_timebase) { aac_cmd_timeout(softs, acp); time_out = 1; time_adjust = aac_tick * drv_usectohz(1000000); break; /* timeout happened */ } else { break; /* no timeout */ } } } while (time_out); mutex_enter(&softs->time_mutex); softs->time_out = softs->timebase + aac_tick; mutex_exit(&softs->time_mutex); } /* * The event thread handles various tasks serially for the other parts of * the driver, so that they can run fast. */ static void aac_event_thread(struct aac_softstate *softs) { int run = 1; DBCALLED(softs, 1); mutex_enter(&softs->ev_lock); while (run) { int events; if ((events = softs->events) == 0) { cv_wait(&softs->event_disp_cv, &softs->ev_lock); events = softs->events; } softs->events = 0; mutex_exit(&softs->ev_lock); mutex_enter(&softs->io_lock); if ((softs->state & AAC_STATE_RUN) && (softs->state & AAC_STATE_DEAD) == 0) { if (events & AAC_EVENT_TIMEOUT) aac_daemon(softs); if (events & AAC_EVENT_SYNCTICK) aac_sync_tick(softs); if (events & AAC_EVENT_AIF) aac_aif_event(softs); } else { run = 0; } mutex_exit(&softs->io_lock); mutex_enter(&softs->ev_lock); } cv_signal(&softs->event_wait_cv); mutex_exit(&softs->ev_lock); } /* * Internal timer. It is only responsbile for time counting and report time * related events. Events handling is done by aac_event_thread(), so that * the timer itself could be as precise as possible. */ static void aac_timer(void *arg) { struct aac_softstate *softs = arg; int events = 0; mutex_enter(&softs->time_mutex); /* If timer is being stopped, exit */ if (softs->timeout_id) { softs->timeout_id = timeout(aac_timer, (void *)softs, (aac_tick * drv_usectohz(1000000))); } else { mutex_exit(&softs->time_mutex); return; } /* Time counting */ softs->timebase += aac_tick; /* Check time related events */ if (softs->time_out && softs->time_out <= softs->timebase) events |= AAC_EVENT_TIMEOUT; if (softs->time_sync && softs->time_sync <= softs->timebase) events |= AAC_EVENT_SYNCTICK; mutex_exit(&softs->time_mutex); if (events) aac_event_disp(softs, events); } /* * Dispatch events to daemon thread for handling */ static void aac_event_disp(struct aac_softstate *softs, int events) { mutex_enter(&softs->ev_lock); softs->events |= events; cv_broadcast(&softs->event_disp_cv); mutex_exit(&softs->ev_lock); } /* * Architecture dependent functions */ static int aac_rx_get_fwstatus(struct aac_softstate *softs) { return (PCI_MEM_GET32(softs, AAC_OMR0)); } static int aac_rx_get_mailbox(struct aac_softstate *softs, int mb) { return (PCI_MEM_GET32(softs, AAC_RX_MAILBOX + mb * 4)); } static void aac_rx_set_mailbox(struct aac_softstate *softs, uint32_t cmd, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3) { PCI_MEM_PUT32(softs, AAC_RX_MAILBOX, cmd); PCI_MEM_PUT32(softs, AAC_RX_MAILBOX + 4, arg0); PCI_MEM_PUT32(softs, AAC_RX_MAILBOX + 8, arg1); PCI_MEM_PUT32(softs, AAC_RX_MAILBOX + 12, arg2); PCI_MEM_PUT32(softs, AAC_RX_MAILBOX + 16, arg3); } static int aac_rkt_get_fwstatus(struct aac_softstate *softs) { return (PCI_MEM_GET32(softs, AAC_OMR0)); } static int aac_rkt_get_mailbox(struct aac_softstate *softs, int mb) { return (PCI_MEM_GET32(softs, AAC_RKT_MAILBOX + mb *4)); } static void aac_rkt_set_mailbox(struct aac_softstate *softs, uint32_t cmd, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3) { PCI_MEM_PUT32(softs, AAC_RKT_MAILBOX, cmd); PCI_MEM_PUT32(softs, AAC_RKT_MAILBOX + 4, arg0); PCI_MEM_PUT32(softs, AAC_RKT_MAILBOX + 8, arg1); PCI_MEM_PUT32(softs, AAC_RKT_MAILBOX + 12, arg2); PCI_MEM_PUT32(softs, AAC_RKT_MAILBOX + 16, arg3); } /* * cb_ops functions */ static int aac_open(dev_t *devp, int flag, int otyp, cred_t *cred) { struct aac_softstate *softs; int minor0, minor; int instance; DBCALLED(NULL, 2); if (otyp != OTYP_BLK && otyp != OTYP_CHR) return (EINVAL); minor0 = getminor(*devp); minor = AAC_SCSA_MINOR(minor0); if (AAC_IS_SCSA_NODE(minor)) return (scsi_hba_open(devp, flag, otyp, cred)); instance = MINOR2INST(minor0); if (instance >= AAC_MAX_ADAPTERS) return (ENXIO); softs = ddi_get_soft_state(aac_softstatep, instance); if (softs == NULL) return (ENXIO); return (0); } /*ARGSUSED*/ static int aac_close(dev_t dev, int flag, int otyp, cred_t *cred) { int minor0, minor; int instance; DBCALLED(NULL, 2); if (otyp != OTYP_BLK && otyp != OTYP_CHR) return (EINVAL); minor0 = getminor(dev); minor = AAC_SCSA_MINOR(minor0); if (AAC_IS_SCSA_NODE(minor)) return (scsi_hba_close(dev, flag, otyp, cred)); instance = MINOR2INST(minor0); if (instance >= AAC_MAX_ADAPTERS) return (ENXIO); return (0); } static int aac_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p, int *rval_p) { struct aac_softstate *softs; int minor0, minor; int instance; DBCALLED(NULL, 2); if (drv_priv(cred_p) != 0) return (EPERM); minor0 = getminor(dev); minor = AAC_SCSA_MINOR(minor0); if (AAC_IS_SCSA_NODE(minor)) return (scsi_hba_ioctl(dev, cmd, arg, flag, cred_p, rval_p)); instance = MINOR2INST(minor0); if (instance < AAC_MAX_ADAPTERS) { softs = ddi_get_soft_state(aac_softstatep, instance); return (aac_do_ioctl(softs, dev, cmd, arg, flag)); } return (ENXIO); } /* * The IO fault service error handling callback function */ /*ARGSUSED*/ static int aac_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data) { /* * as the driver can always deal with an error in any dma or * access handle, we can just return the fme_status value. */ pci_ereport_post(dip, err, NULL); return (err->fme_status); } /* * aac_fm_init - initialize fma capabilities and register with IO * fault services. */ static void aac_fm_init(struct aac_softstate *softs) { /* * Need to change iblock to priority for new MSI intr */ ddi_iblock_cookie_t fm_ibc; softs->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, softs->devinfo_p, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "fm-capable", DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE); /* Only register with IO Fault Services if we have some capability */ if (softs->fm_capabilities) { /* Adjust access and dma attributes for FMA */ softs->reg_attr.devacc_attr_access = DDI_FLAGERR_ACC; softs->addr_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; softs->buf_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; /* * Register capabilities with IO Fault Services. * fm_capabilities will be updated to indicate * capabilities actually supported (not requested.) */ ddi_fm_init(softs->devinfo_p, &softs->fm_capabilities, &fm_ibc); /* * Initialize pci ereport capabilities if ereport * capable (should always be.) */ if (DDI_FM_EREPORT_CAP(softs->fm_capabilities) || DDI_FM_ERRCB_CAP(softs->fm_capabilities)) { pci_ereport_setup(softs->devinfo_p); } /* * Register error callback if error callback capable. */ if (DDI_FM_ERRCB_CAP(softs->fm_capabilities)) { ddi_fm_handler_register(softs->devinfo_p, aac_fm_error_cb, (void *) softs); } } } /* * aac_fm_fini - Releases fma capabilities and un-registers with IO * fault services. */ static void aac_fm_fini(struct aac_softstate *softs) { /* Only unregister FMA capabilities if registered */ if (softs->fm_capabilities) { /* * Un-register error callback if error callback capable. */ if (DDI_FM_ERRCB_CAP(softs->fm_capabilities)) { ddi_fm_handler_unregister(softs->devinfo_p); } /* * Release any resources allocated by pci_ereport_setup() */ if (DDI_FM_EREPORT_CAP(softs->fm_capabilities) || DDI_FM_ERRCB_CAP(softs->fm_capabilities)) { pci_ereport_teardown(softs->devinfo_p); } /* Unregister from IO Fault Services */ ddi_fm_fini(softs->devinfo_p); /* Adjust access and dma attributes for FMA */ softs->reg_attr.devacc_attr_access = DDI_DEFAULT_ACC; softs->addr_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; softs->buf_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; } } int aac_check_acc_handle(ddi_acc_handle_t handle) { ddi_fm_error_t de; ddi_fm_acc_err_get(handle, &de, DDI_FME_VERSION); return (de.fme_status); } int aac_check_dma_handle(ddi_dma_handle_t handle) { ddi_fm_error_t de; ddi_fm_dma_err_get(handle, &de, DDI_FME_VERSION); return (de.fme_status); } void aac_fm_ereport(struct aac_softstate *softs, char *detail) { uint64_t ena; char buf[FM_MAX_CLASS]; (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail); ena = fm_ena_generate(0, FM_ENA_FMT1); if (DDI_FM_EREPORT_CAP(softs->fm_capabilities)) { ddi_fm_ereport_post(softs->devinfo_p, buf, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERSION, NULL); } } /* * Autoconfiguration support */ static int aac_parse_devname(char *devnm, int *tgt, int *lun) { char devbuf[SCSI_MAXNAMELEN]; char *addr; char *p, *tp, *lp; long num; /* Parse dev name and address */ (void) strcpy(devbuf, devnm); addr = ""; for (p = devbuf; *p != '\0'; p++) { if (*p == '@') { addr = p + 1; *p = '\0'; } else if (*p == ':') { *p = '\0'; break; } } /* Parse taget and lun */ for (p = tp = addr, lp = NULL; *p != '\0'; p++) { if (*p == ',') { lp = p + 1; *p = '\0'; break; } } if (tgt && tp) { if (ddi_strtol(tp, NULL, 0x10, &num)) return (AACERR); *tgt = (int)num; } if (lun && lp) { if (ddi_strtol(lp, NULL, 0x10, &num)) return (AACERR); *lun = (int)num; } return (AACOK); } static dev_info_t * aac_find_child(struct aac_softstate *softs, uint16_t tgt, uint8_t lun) { dev_info_t *child = NULL; char addr[SCSI_MAXNAMELEN]; char tmp[MAXNAMELEN]; if (tgt < AAC_MAX_LD) { if (lun == 0) { struct aac_device *dvp = &softs->containers[tgt].dev; child = dvp->dip; } } else { (void) sprintf(addr, "%x,%x", tgt, lun); for (child = ddi_get_child(softs->devinfo_p); child; child = ddi_get_next_sibling(child)) { /* We don't care about non-persistent node */ if (ndi_dev_is_persistent_node(child) == 0) continue; if (aac_name_node(child, tmp, MAXNAMELEN) != DDI_SUCCESS) continue; if (strcmp(addr, tmp) == 0) break; } } return (child); } static int aac_config_child(struct aac_softstate *softs, struct scsi_device *sd, dev_info_t **dipp) { char *nodename = NULL; char **compatible = NULL; int ncompatible = 0; char *childname; dev_info_t *ldip = NULL; int tgt = sd->sd_address.a_target; int lun = sd->sd_address.a_lun; int dtype = sd->sd_inq->inq_dtype & DTYPE_MASK; int rval; DBCALLED(softs, 2); scsi_hba_nodename_compatible_get(sd->sd_inq, NULL, dtype, NULL, &nodename, &compatible, &ncompatible); if (nodename == NULL) { AACDB_PRINT(softs, CE_WARN, "found no comptible driver for t%dL%d", tgt, lun); rval = NDI_FAILURE; goto finish; } childname = (softs->legacy && dtype == DTYPE_DIRECT) ? "sd" : nodename; /* Create dev node */ rval = ndi_devi_alloc(softs->devinfo_p, childname, DEVI_SID_NODEID, &ldip); if (rval == NDI_SUCCESS) { if (ndi_prop_update_int(DDI_DEV_T_NONE, ldip, "target", tgt) != DDI_PROP_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "unable to create " "property for t%dL%d (target)", tgt, lun); rval = NDI_FAILURE; goto finish; } if (ndi_prop_update_int(DDI_DEV_T_NONE, ldip, "lun", lun) != DDI_PROP_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "unable to create " "property for t%dL%d (lun)", tgt, lun); rval = NDI_FAILURE; goto finish; } if (ndi_prop_update_string_array(DDI_DEV_T_NONE, ldip, "compatible", compatible, ncompatible) != DDI_PROP_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "unable to create " "property for t%dL%d (compatible)", tgt, lun); rval = NDI_FAILURE; goto finish; } rval = ndi_devi_online(ldip, NDI_ONLINE_ATTACH); if (rval != NDI_SUCCESS) { AACDB_PRINT(softs, CE_WARN, "unable to online t%dL%d", tgt, lun); ndi_prop_remove_all(ldip); (void) ndi_devi_free(ldip); } } finish: if (dipp) *dipp = ldip; scsi_hba_nodename_compatible_free(nodename, compatible); return (rval); } /*ARGSUSED*/ static int aac_probe_lun(struct aac_softstate *softs, struct scsi_device *sd) { int tgt = sd->sd_address.a_target; int lun = sd->sd_address.a_lun; DBCALLED(softs, 2); if (tgt < AAC_MAX_LD) { enum aac_cfg_event event; if (lun == 0) { mutex_enter(&softs->io_lock); event = aac_probe_container(softs, tgt); mutex_exit(&softs->io_lock); if ((event != AAC_CFG_NULL_NOEXIST) && (event != AAC_CFG_DELETE)) { if (scsi_hba_probe(sd, NULL) == SCSIPROBE_EXISTS) return (NDI_SUCCESS); } } return (NDI_FAILURE); } else { int dtype; int qual; /* device qualifier */ if (scsi_hba_probe(sd, NULL) != SCSIPROBE_EXISTS) return (NDI_FAILURE); dtype = sd->sd_inq->inq_dtype & DTYPE_MASK; qual = dtype >> 5; AACDB_PRINT(softs, CE_NOTE, "Phys. device found: tgt %d dtype %d: %s", tgt, dtype, sd->sd_inq->inq_vid); /* Only non-DASD and JBOD mode DASD are allowed exposed */ if (dtype == DTYPE_RODIRECT /* CDROM */ || dtype == DTYPE_SEQUENTIAL /* TAPE */ || dtype == DTYPE_ESI /* SES */) { if (!(softs->flags & AAC_FLAGS_NONDASD)) return (NDI_FAILURE); AACDB_PRINT(softs, CE_NOTE, "non-DASD %d found", tgt); } else if (dtype == DTYPE_DIRECT) { if (!(softs->flags & AAC_FLAGS_JBOD) || qual != 0) return (NDI_FAILURE); AACDB_PRINT(softs, CE_NOTE, "JBOD DASD %d found", tgt); } mutex_enter(&softs->io_lock); softs->nondasds[AAC_PD(tgt)].dev.flags |= AAC_DFLAG_VALID; mutex_exit(&softs->io_lock); return (NDI_SUCCESS); } } static int aac_config_lun(struct aac_softstate *softs, uint16_t tgt, uint8_t lun, dev_info_t **ldip) { struct scsi_device sd; dev_info_t *child; int rval; DBCALLED(softs, 2); if ((child = aac_find_child(softs, tgt, lun)) != NULL) { if (ldip) *ldip = child; return (NDI_SUCCESS); } bzero(&sd, sizeof (struct scsi_device)); sd.sd_address.a_hba_tran = softs->hba_tran; sd.sd_address.a_target = (uint16_t)tgt; sd.sd_address.a_lun = (uint8_t)lun; if ((rval = aac_probe_lun(softs, &sd)) == NDI_SUCCESS) rval = aac_config_child(softs, &sd, ldip); /* scsi_unprobe is blank now. Free buffer manually */ if (sd.sd_inq) { kmem_free(sd.sd_inq, SUN_INQSIZE); sd.sd_inq = (struct scsi_inquiry *)NULL; } return (rval); } static int aac_config_tgt(struct aac_softstate *softs, int tgt) { struct scsi_address ap; struct buf *bp = NULL; int buf_len = AAC_SCSI_RPTLUNS_HEAD_SIZE + AAC_SCSI_RPTLUNS_ADDR_SIZE; int list_len = 0; int lun_total = 0; dev_info_t *ldip; int i; ap.a_hba_tran = softs->hba_tran; ap.a_target = (uint16_t)tgt; ap.a_lun = 0; for (i = 0; i < 2; i++) { struct scsi_pkt *pkt; uchar_t *cdb; uchar_t *p; uint32_t data; if (bp == NULL) { if ((bp = scsi_alloc_consistent_buf(&ap, NULL, buf_len, B_READ, NULL_FUNC, NULL)) == NULL) return (AACERR); } if ((pkt = scsi_init_pkt(&ap, NULL, bp, CDB_GROUP5, sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT, NULL, NULL)) == NULL) { scsi_free_consistent_buf(bp); return (AACERR); } cdb = pkt->pkt_cdbp; bzero(cdb, CDB_GROUP5); cdb[0] = SCMD_REPORT_LUNS; /* Convert buffer len from local to LE_32 */ data = buf_len; for (p = &cdb[9]; p > &cdb[5]; p--) { *p = data & 0xff; data >>= 8; } if (scsi_poll(pkt) < 0 || ((struct scsi_status *)pkt->pkt_scbp)->sts_chk) { scsi_destroy_pkt(pkt); break; } /* Convert list_len from LE_32 to local */ for (p = (uchar_t *)bp->b_un.b_addr; p < (uchar_t *)bp->b_un.b_addr + 4; p++) { data <<= 8; data |= *p; } list_len = data; if (buf_len < list_len + AAC_SCSI_RPTLUNS_HEAD_SIZE) { scsi_free_consistent_buf(bp); bp = NULL; buf_len = list_len + AAC_SCSI_RPTLUNS_HEAD_SIZE; } scsi_destroy_pkt(pkt); } if (i >= 2) { uint8_t *buf = (uint8_t *)(bp->b_un.b_addr + AAC_SCSI_RPTLUNS_HEAD_SIZE); for (i = 0; i < (list_len / AAC_SCSI_RPTLUNS_ADDR_SIZE); i++) { uint16_t lun; /* Determine report luns addressing type */ switch (buf[0] & AAC_SCSI_RPTLUNS_ADDR_MASK) { /* * Vendors in the field have been found to be * concatenating bus/target/lun to equal the * complete lun value instead of switching to * flat space addressing */ case AAC_SCSI_RPTLUNS_ADDR_PERIPHERAL: case AAC_SCSI_RPTLUNS_ADDR_LOGICAL_UNIT: case AAC_SCSI_RPTLUNS_ADDR_FLAT_SPACE: lun = ((buf[0] & 0x3f) << 8) | buf[1]; if (lun > UINT8_MAX) { AACDB_PRINT(softs, CE_WARN, "abnormal lun number: %d", lun); break; } if (aac_config_lun(softs, tgt, lun, &ldip) == NDI_SUCCESS) lun_total++; break; } buf += AAC_SCSI_RPTLUNS_ADDR_SIZE; } } else { /* The target may do not support SCMD_REPORT_LUNS. */ if (aac_config_lun(softs, tgt, 0, &ldip) == NDI_SUCCESS) lun_total++; } scsi_free_consistent_buf(bp); return (lun_total); } static void aac_devcfg(struct aac_softstate *softs, int tgt, int en) { struct aac_device *dvp; mutex_enter(&softs->io_lock); dvp = AAC_DEV(softs, tgt); if (en) dvp->flags |= AAC_DFLAG_CONFIGURING; else dvp->flags &= ~AAC_DFLAG_CONFIGURING; mutex_exit(&softs->io_lock); } static int aac_tran_bus_config(dev_info_t *parent, uint_t flags, ddi_bus_config_op_t op, void *arg, dev_info_t **childp) { struct aac_softstate *softs; int rval = NDI_FAILURE; if ((softs = ddi_get_soft_state(aac_softstatep, ddi_get_instance(parent))) == NULL) return (NDI_FAILURE); /* Commands for bus config should be blocked as the bus is quiesced */ mutex_enter(&softs->io_lock); if (softs->state & AAC_STATE_QUIESCED) { AACDB_PRINT(softs, CE_NOTE, "bus_config aborted because bus is quiesced"); mutex_exit(&softs->io_lock); return (NDI_FAILURE); } mutex_exit(&softs->io_lock); DBCALLED(softs, 1); /* Hold the nexus across the bus_config */ ndi_devi_enter(parent); switch (op) { case BUS_CONFIG_ONE: { int tgt, lun; if (aac_parse_devname(arg, &tgt, &lun) != AACOK) { rval = NDI_FAILURE; break; } if (tgt >= AAC_MAX_LD) { if (tgt >= AAC_MAX_DEV(softs)) { rval = NDI_FAILURE; break; } } AAC_DEVCFG_BEGIN(softs, tgt); rval = aac_config_lun(softs, tgt, lun, childp); AAC_DEVCFG_END(softs, tgt); break; } case BUS_CONFIG_DRIVER: case BUS_CONFIG_ALL: { uint32_t bus, tgt; int index, total; for (tgt = 0; tgt < AAC_MAX_LD; tgt++) { AAC_DEVCFG_BEGIN(softs, tgt); (void) aac_config_lun(softs, tgt, 0, NULL); AAC_DEVCFG_END(softs, tgt); } /* Config the non-DASD devices connected to the card */ total = 0; index = AAC_MAX_LD; for (bus = 0; bus < softs->bus_max; bus++) { AACDB_PRINT(softs, CE_NOTE, "bus %d:", bus); for (tgt = 0; tgt < softs->tgt_max; tgt++, index++) { AAC_DEVCFG_BEGIN(softs, index); if (aac_config_tgt(softs, index)) total++; AAC_DEVCFG_END(softs, index); } } AACDB_PRINT(softs, CE_CONT, "?Total %d phys. device(s) found", total); rval = NDI_SUCCESS; break; } } if (rval == NDI_SUCCESS) rval = ndi_busop_bus_config(parent, flags, op, arg, childp, 0); ndi_devi_exit(parent); return (rval); } /*ARGSUSED*/ static int aac_handle_dr(struct aac_softstate *softs, int tgt, int lun, int event) { struct aac_device *dvp; dev_info_t *dip; int valid; DBCALLED(softs, 1); /* Hold the nexus across the bus_config */ dvp = AAC_DEV(softs, tgt); valid = AAC_DEV_IS_VALID(dvp); dip = dvp->dip; if (!(softs->state & AAC_STATE_RUN)) return (AACERR); mutex_exit(&softs->io_lock); switch (event) { case AAC_CFG_ADD: case AAC_CFG_DELETE: /* Device onlined */ if (dip == NULL && valid) { ndi_devi_enter(softs->devinfo_p); (void) aac_config_lun(softs, tgt, 0, NULL); AACDB_PRINT(softs, CE_NOTE, "c%dt%dL%d onlined", softs->instance, tgt, lun); ndi_devi_exit(softs->devinfo_p); } /* Device offlined */ if (dip && !valid) { mutex_enter(&softs->io_lock); (void) aac_do_reset(softs); mutex_exit(&softs->io_lock); (void) ndi_devi_offline(dip, NDI_DEVI_REMOVE); AACDB_PRINT(softs, CE_NOTE, "c%dt%dL%d offlined", softs->instance, tgt, lun); } break; } mutex_enter(&softs->io_lock); return (AACOK); } #ifdef DEBUG /* -------------------------debug aid functions-------------------------- */ #define AAC_FIB_CMD_KEY_STRINGS \ TestCommandResponse, "TestCommandResponse", \ TestAdapterCommand, "TestAdapterCommand", \ LastTestCommand, "LastTestCommand", \ ReinitHostNormCommandQueue, "ReinitHostNormCommandQueue", \ ReinitHostHighCommandQueue, "ReinitHostHighCommandQueue", \ ReinitHostHighRespQueue, "ReinitHostHighRespQueue", \ ReinitHostNormRespQueue, "ReinitHostNormRespQueue", \ ReinitAdapNormCommandQueue, "ReinitAdapNormCommandQueue", \ ReinitAdapHighCommandQueue, "ReinitAdapHighCommandQueue", \ ReinitAdapHighRespQueue, "ReinitAdapHighRespQueue", \ ReinitAdapNormRespQueue, "ReinitAdapNormRespQueue", \ InterfaceShutdown, "InterfaceShutdown", \ DmaCommandFib, "DmaCommandFib", \ StartProfile, "StartProfile", \ TermProfile, "TermProfile", \ SpeedTest, "SpeedTest", \ TakeABreakPt, "TakeABreakPt", \ RequestPerfData, "RequestPerfData", \ SetInterruptDefTimer, "SetInterruptDefTimer", \ SetInterruptDefCount, "SetInterruptDefCount", \ GetInterruptDefStatus, "GetInterruptDefStatus", \ LastCommCommand, "LastCommCommand", \ NuFileSystem, "NuFileSystem", \ UFS, "UFS", \ HostFileSystem, "HostFileSystem", \ LastFileSystemCommand, "LastFileSystemCommand", \ ContainerCommand, "ContainerCommand", \ ContainerCommand64, "ContainerCommand64", \ ClusterCommand, "ClusterCommand", \ ScsiPortCommand, "ScsiPortCommand", \ ScsiPortCommandU64, "ScsiPortCommandU64", \ AifRequest, "AifRequest", \ CheckRevision, "CheckRevision", \ FsaHostShutdown, "FsaHostShutdown", \ RequestAdapterInfo, "RequestAdapterInfo", \ IsAdapterPaused, "IsAdapterPaused", \ SendHostTime, "SendHostTime", \ LastMiscCommand, "LastMiscCommand" #define AAC_CTVM_SUBCMD_KEY_STRINGS \ VM_Null, "VM_Null", \ VM_NameServe, "VM_NameServe", \ VM_ContainerConfig, "VM_ContainerConfig", \ VM_Ioctl, "VM_Ioctl", \ VM_FilesystemIoctl, "VM_FilesystemIoctl", \ VM_CloseAll, "VM_CloseAll", \ VM_CtBlockRead, "VM_CtBlockRead", \ VM_CtBlockWrite, "VM_CtBlockWrite", \ VM_SliceBlockRead, "VM_SliceBlockRead", \ VM_SliceBlockWrite, "VM_SliceBlockWrite", \ VM_DriveBlockRead, "VM_DriveBlockRead", \ VM_DriveBlockWrite, "VM_DriveBlockWrite", \ VM_EnclosureMgt, "VM_EnclosureMgt", \ VM_Unused, "VM_Unused", \ VM_CtBlockVerify, "VM_CtBlockVerify", \ VM_CtPerf, "VM_CtPerf", \ VM_CtBlockRead64, "VM_CtBlockRead64", \ VM_CtBlockWrite64, "VM_CtBlockWrite64", \ VM_CtBlockVerify64, "VM_CtBlockVerify64", \ VM_CtHostRead64, "VM_CtHostRead64", \ VM_CtHostWrite64, "VM_CtHostWrite64", \ VM_NameServe64, "VM_NameServe64" #define AAC_CT_SUBCMD_KEY_STRINGS \ CT_Null, "CT_Null", \ CT_GET_SLICE_COUNT, "CT_GET_SLICE_COUNT", \ CT_GET_PARTITION_COUNT, "CT_GET_PARTITION_COUNT", \ CT_GET_PARTITION_INFO, "CT_GET_PARTITION_INFO", \ CT_GET_CONTAINER_COUNT, "CT_GET_CONTAINER_COUNT", \ CT_GET_CONTAINER_INFO_OLD, "CT_GET_CONTAINER_INFO_OLD", \ CT_WRITE_MBR, "CT_WRITE_MBR", \ CT_WRITE_PARTITION, "CT_WRITE_PARTITION", \ CT_UPDATE_PARTITION, "CT_UPDATE_PARTITION", \ CT_UNLOAD_CONTAINER, "CT_UNLOAD_CONTAINER", \ CT_CONFIG_SINGLE_PRIMARY, "CT_CONFIG_SINGLE_PRIMARY", \ CT_READ_CONFIG_AGE, "CT_READ_CONFIG_AGE", \ CT_WRITE_CONFIG_AGE, "CT_WRITE_CONFIG_AGE", \ CT_READ_SERIAL_NUMBER, "CT_READ_SERIAL_NUMBER", \ CT_ZERO_PAR_ENTRY, "CT_ZERO_PAR_ENTRY", \ CT_READ_MBR, "CT_READ_MBR", \ CT_READ_PARTITION, "CT_READ_PARTITION", \ CT_DESTROY_CONTAINER, "CT_DESTROY_CONTAINER", \ CT_DESTROY2_CONTAINER, "CT_DESTROY2_CONTAINER", \ CT_SLICE_SIZE, "CT_SLICE_SIZE", \ CT_CHECK_CONFLICTS, "CT_CHECK_CONFLICTS", \ CT_MOVE_CONTAINER, "CT_MOVE_CONTAINER", \ CT_READ_LAST_DRIVE, "CT_READ_LAST_DRIVE", \ CT_WRITE_LAST_DRIVE, "CT_WRITE_LAST_DRIVE", \ CT_UNMIRROR, "CT_UNMIRROR", \ CT_MIRROR_DELAY, "CT_MIRROR_DELAY", \ CT_GEN_MIRROR, "CT_GEN_MIRROR", \ CT_GEN_MIRROR2, "CT_GEN_MIRROR2", \ CT_TEST_CONTAINER, "CT_TEST_CONTAINER", \ CT_MOVE2, "CT_MOVE2", \ CT_SPLIT, "CT_SPLIT", \ CT_SPLIT2, "CT_SPLIT2", \ CT_SPLIT_BROKEN, "CT_SPLIT_BROKEN", \ CT_SPLIT_BROKEN2, "CT_SPLIT_BROKEN2", \ CT_RECONFIG, "CT_RECONFIG", \ CT_BREAK2, "CT_BREAK2", \ CT_BREAK, "CT_BREAK", \ CT_MERGE2, "CT_MERGE2", \ CT_MERGE, "CT_MERGE", \ CT_FORCE_ERROR, "CT_FORCE_ERROR", \ CT_CLEAR_ERROR, "CT_CLEAR_ERROR", \ CT_ASSIGN_FAILOVER, "CT_ASSIGN_FAILOVER", \ CT_CLEAR_FAILOVER, "CT_CLEAR_FAILOVER", \ CT_GET_FAILOVER_DATA, "CT_GET_FAILOVER_DATA", \ CT_VOLUME_ADD, "CT_VOLUME_ADD", \ CT_VOLUME_ADD2, "CT_VOLUME_ADD2", \ CT_MIRROR_STATUS, "CT_MIRROR_STATUS", \ CT_COPY_STATUS, "CT_COPY_STATUS", \ CT_COPY, "CT_COPY", \ CT_UNLOCK_CONTAINER, "CT_UNLOCK_CONTAINER", \ CT_LOCK_CONTAINER, "CT_LOCK_CONTAINER", \ CT_MAKE_READ_ONLY, "CT_MAKE_READ_ONLY", \ CT_MAKE_READ_WRITE, "CT_MAKE_READ_WRITE", \ CT_CLEAN_DEAD, "CT_CLEAN_DEAD", \ CT_ABORT_MIRROR_COMMAND, "CT_ABORT_MIRROR_COMMAND", \ CT_SET, "CT_SET", \ CT_GET, "CT_GET", \ CT_GET_NVLOG_ENTRY, "CT_GET_NVLOG_ENTRY", \ CT_GET_DELAY, "CT_GET_DELAY", \ CT_ZERO_CONTAINER_SPACE, "CT_ZERO_CONTAINER_SPACE", \ CT_GET_ZERO_STATUS, "CT_GET_ZERO_STATUS", \ CT_SCRUB, "CT_SCRUB", \ CT_GET_SCRUB_STATUS, "CT_GET_SCRUB_STATUS", \ CT_GET_SLICE_INFO, "CT_GET_SLICE_INFO", \ CT_GET_SCSI_METHOD, "CT_GET_SCSI_METHOD", \ CT_PAUSE_IO, "CT_PAUSE_IO", \ CT_RELEASE_IO, "CT_RELEASE_IO", \ CT_SCRUB2, "CT_SCRUB2", \ CT_MCHECK, "CT_MCHECK", \ CT_CORRUPT, "CT_CORRUPT", \ CT_GET_TASK_COUNT, "CT_GET_TASK_COUNT", \ CT_PROMOTE, "CT_PROMOTE", \ CT_SET_DEAD, "CT_SET_DEAD", \ CT_CONTAINER_OPTIONS, "CT_CONTAINER_OPTIONS", \ CT_GET_NV_PARAM, "CT_GET_NV_PARAM", \ CT_GET_PARAM, "CT_GET_PARAM", \ CT_NV_PARAM_SIZE, "CT_NV_PARAM_SIZE", \ CT_COMMON_PARAM_SIZE, "CT_COMMON_PARAM_SIZE", \ CT_PLATFORM_PARAM_SIZE, "CT_PLATFORM_PARAM_SIZE", \ CT_SET_NV_PARAM, "CT_SET_NV_PARAM", \ CT_ABORT_SCRUB, "CT_ABORT_SCRUB", \ CT_GET_SCRUB_ERROR, "CT_GET_SCRUB_ERROR", \ CT_LABEL_CONTAINER, "CT_LABEL_CONTAINER", \ CT_CONTINUE_DATA, "CT_CONTINUE_DATA", \ CT_STOP_DATA, "CT_STOP_DATA", \ CT_GET_PARTITION_TABLE, "CT_GET_PARTITION_TABLE", \ CT_GET_DISK_PARTITIONS, "CT_GET_DISK_PARTITIONS", \ CT_GET_MISC_STATUS, "CT_GET_MISC_STATUS", \ CT_GET_CONTAINER_PERF_INFO, "CT_GET_CONTAINER_PERF_INFO", \ CT_GET_TIME, "CT_GET_TIME", \ CT_READ_DATA, "CT_READ_DATA", \ CT_CTR, "CT_CTR", \ CT_CTL, "CT_CTL", \ CT_DRAINIO, "CT_DRAINIO", \ CT_RELEASEIO, "CT_RELEASEIO", \ CT_GET_NVRAM, "CT_GET_NVRAM", \ CT_GET_MEMORY, "CT_GET_MEMORY", \ CT_PRINT_CT_LOG, "CT_PRINT_CT_LOG", \ CT_ADD_LEVEL, "CT_ADD_LEVEL", \ CT_NV_ZERO, "CT_NV_ZERO", \ CT_READ_SIGNATURE, "CT_READ_SIGNATURE", \ CT_THROTTLE_ON, "CT_THROTTLE_ON", \ CT_THROTTLE_OFF, "CT_THROTTLE_OFF", \ CT_GET_THROTTLE_STATS, "CT_GET_THROTTLE_STATS", \ CT_MAKE_SNAPSHOT, "CT_MAKE_SNAPSHOT", \ CT_REMOVE_SNAPSHOT, "CT_REMOVE_SNAPSHOT", \ CT_WRITE_USER_FLAGS, "CT_WRITE_USER_FLAGS", \ CT_READ_USER_FLAGS, "CT_READ_USER_FLAGS", \ CT_MONITOR, "CT_MONITOR", \ CT_GEN_MORPH, "CT_GEN_MORPH", \ CT_GET_SNAPSHOT_INFO, "CT_GET_SNAPSHOT_INFO", \ CT_CACHE_SET, "CT_CACHE_SET", \ CT_CACHE_STAT, "CT_CACHE_STAT", \ CT_TRACE_START, "CT_TRACE_START", \ CT_TRACE_STOP, "CT_TRACE_STOP", \ CT_TRACE_ENABLE, "CT_TRACE_ENABLE", \ CT_TRACE_DISABLE, "CT_TRACE_DISABLE", \ CT_FORCE_CORE_DUMP, "CT_FORCE_CORE_DUMP", \ CT_SET_SERIAL_NUMBER, "CT_SET_SERIAL_NUMBER", \ CT_RESET_SERIAL_NUMBER, "CT_RESET_SERIAL_NUMBER", \ CT_ENABLE_RAID5, "CT_ENABLE_RAID5", \ CT_CLEAR_VALID_DUMP_FLAG, "CT_CLEAR_VALID_DUMP_FLAG", \ CT_GET_MEM_STATS, "CT_GET_MEM_STATS", \ CT_GET_CORE_SIZE, "CT_GET_CORE_SIZE", \ CT_CREATE_CONTAINER_OLD, "CT_CREATE_CONTAINER_OLD", \ CT_STOP_DUMPS, "CT_STOP_DUMPS", \ CT_PANIC_ON_TAKE_A_BREAK, "CT_PANIC_ON_TAKE_A_BREAK", \ CT_GET_CACHE_STATS, "CT_GET_CACHE_STATS", \ CT_MOVE_PARTITION, "CT_MOVE_PARTITION", \ CT_FLUSH_CACHE, "CT_FLUSH_CACHE", \ CT_READ_NAME, "CT_READ_NAME", \ CT_WRITE_NAME, "CT_WRITE_NAME", \ CT_TOSS_CACHE, "CT_TOSS_CACHE", \ CT_LOCK_DRAINIO, "CT_LOCK_DRAINIO", \ CT_CONTAINER_OFFLINE, "CT_CONTAINER_OFFLINE", \ CT_SET_CACHE_SIZE, "CT_SET_CACHE_SIZE", \ CT_CLEAN_SHUTDOWN_STATUS, "CT_CLEAN_SHUTDOWN_STATUS", \ CT_CLEAR_DISKLOG_ON_DISK, "CT_CLEAR_DISKLOG_ON_DISK", \ CT_CLEAR_ALL_DISKLOG, "CT_CLEAR_ALL_DISKLOG", \ CT_CACHE_FAVOR, "CT_CACHE_FAVOR", \ CT_READ_PASSTHRU_MBR, "CT_READ_PASSTHRU_MBR", \ CT_SCRUB_NOFIX, "CT_SCRUB_NOFIX", \ CT_SCRUB2_NOFIX, "CT_SCRUB2_NOFIX", \ CT_FLUSH, "CT_FLUSH", \ CT_REBUILD, "CT_REBUILD", \ CT_FLUSH_CONTAINER, "CT_FLUSH_CONTAINER", \ CT_RESTART, "CT_RESTART", \ CT_GET_CONFIG_STATUS, "CT_GET_CONFIG_STATUS", \ CT_TRACE_FLAG, "CT_TRACE_FLAG", \ CT_RESTART_MORPH, "CT_RESTART_MORPH", \ CT_GET_TRACE_INFO, "CT_GET_TRACE_INFO", \ CT_GET_TRACE_ITEM, "CT_GET_TRACE_ITEM", \ CT_COMMIT_CONFIG, "CT_COMMIT_CONFIG", \ CT_CONTAINER_EXISTS, "CT_CONTAINER_EXISTS", \ CT_GET_SLICE_FROM_DEVT, "CT_GET_SLICE_FROM_DEVT", \ CT_OPEN_READ_WRITE, "CT_OPEN_READ_WRITE", \ CT_WRITE_MEMORY_BLOCK, "CT_WRITE_MEMORY_BLOCK", \ CT_GET_CACHE_PARAMS, "CT_GET_CACHE_PARAMS", \ CT_CRAZY_CACHE, "CT_CRAZY_CACHE", \ CT_GET_PROFILE_STRUCT, "CT_GET_PROFILE_STRUCT", \ CT_SET_IO_TRACE_FLAG, "CT_SET_IO_TRACE_FLAG", \ CT_GET_IO_TRACE_STRUCT, "CT_GET_IO_TRACE_STRUCT", \ CT_CID_TO_64BITS_UID, "CT_CID_TO_64BITS_UID", \ CT_64BITS_UID_TO_CID, "CT_64BITS_UID_TO_CID", \ CT_PAR_TO_64BITS_UID, "CT_PAR_TO_64BITS_UID", \ CT_CID_TO_32BITS_UID, "CT_CID_TO_32BITS_UID", \ CT_32BITS_UID_TO_CID, "CT_32BITS_UID_TO_CID", \ CT_PAR_TO_32BITS_UID, "CT_PAR_TO_32BITS_UID", \ CT_SET_FAILOVER_OPTION, "CT_SET_FAILOVER_OPTION", \ CT_GET_FAILOVER_OPTION, "CT_GET_FAILOVER_OPTION", \ CT_STRIPE_ADD2, "CT_STRIPE_ADD2", \ CT_CREATE_VOLUME_SET, "CT_CREATE_VOLUME_SET", \ CT_CREATE_STRIPE_SET, "CT_CREATE_STRIPE_SET", \ CT_VERIFY_CONTAINER, "CT_VERIFY_CONTAINER", \ CT_IS_CONTAINER_DEAD, "CT_IS_CONTAINER_DEAD", \ CT_GET_CONTAINER_OPTION, "CT_GET_CONTAINER_OPTION", \ CT_GET_SNAPSHOT_UNUSED_STRUCT, "CT_GET_SNAPSHOT_UNUSED_STRUCT", \ CT_CLEAR_SNAPSHOT_UNUSED_STRUCT, "CT_CLEAR_SNAPSHOT_UNUSED_STRUCT", \ CT_GET_CONTAINER_INFO, "CT_GET_CONTAINER_INFO", \ CT_CREATE_CONTAINER, "CT_CREATE_CONTAINER", \ CT_CHANGE_CREATIONINFO, "CT_CHANGE_CREATIONINFO", \ CT_CHECK_CONFLICT_UID, "CT_CHECK_CONFLICT_UID", \ CT_CONTAINER_UID_CHECK, "CT_CONTAINER_UID_CHECK", \ CT_IS_CONTAINER_MEATADATA_STANDARD, \ "CT_IS_CONTAINER_MEATADATA_STANDARD", \ CT_IS_SLICE_METADATA_STANDARD, "CT_IS_SLICE_METADATA_STANDARD", \ CT_GET_IMPORT_COUNT, "CT_GET_IMPORT_COUNT", \ CT_CANCEL_ALL_IMPORTS, "CT_CANCEL_ALL_IMPORTS", \ CT_GET_IMPORT_INFO, "CT_GET_IMPORT_INFO", \ CT_IMPORT_ARRAY, "CT_IMPORT_ARRAY", \ CT_GET_LOG_SIZE, "CT_GET_LOG_SIZE", \ CT_ALARM_GET_STATE, "CT_ALARM_GET_STATE", \ CT_ALARM_SET_STATE, "CT_ALARM_SET_STATE", \ CT_ALARM_ON_OFF, "CT_ALARM_ON_OFF", \ CT_GET_EE_OEM_ID, "CT_GET_EE_OEM_ID", \ CT_GET_PPI_HEADERS, "CT_GET_PPI_HEADERS", \ CT_GET_PPI_DATA, "CT_GET_PPI_DATA", \ CT_GET_PPI_ENTRIES, "CT_GET_PPI_ENTRIES", \ CT_DELETE_PPI_BUNDLE, "CT_DELETE_PPI_BUNDLE", \ CT_GET_PARTITION_TABLE_2, "CT_GET_PARTITION_TABLE_2", \ CT_GET_PARTITION_INFO_2, "CT_GET_PARTITION_INFO_2", \ CT_GET_DISK_PARTITIONS_2, "CT_GET_DISK_PARTITIONS_2", \ CT_QUIESCE_ADAPTER, "CT_QUIESCE_ADAPTER", \ CT_CLEAR_PPI_TABLE, "CT_CLEAR_PPI_TABLE" #define AAC_CL_SUBCMD_KEY_STRINGS \ CL_NULL, "CL_NULL", \ DS_INIT, "DS_INIT", \ DS_RESCAN, "DS_RESCAN", \ DS_CREATE, "DS_CREATE", \ DS_DELETE, "DS_DELETE", \ DS_ADD_DISK, "DS_ADD_DISK", \ DS_REMOVE_DISK, "DS_REMOVE_DISK", \ DS_MOVE_DISK, "DS_MOVE_DISK", \ DS_TAKE_OWNERSHIP, "DS_TAKE_OWNERSHIP", \ DS_RELEASE_OWNERSHIP, "DS_RELEASE_OWNERSHIP", \ DS_FORCE_OWNERSHIP, "DS_FORCE_OWNERSHIP", \ DS_GET_DISK_SET_PARAM, "DS_GET_DISK_SET_PARAM", \ DS_GET_DRIVE_PARAM, "DS_GET_DRIVE_PARAM", \ DS_GET_SLICE_PARAM, "DS_GET_SLICE_PARAM", \ DS_GET_DISK_SETS, "DS_GET_DISK_SETS", \ DS_GET_DRIVES, "DS_GET_DRIVES", \ DS_SET_DISK_SET_PARAM, "DS_SET_DISK_SET_PARAM", \ DS_ONLINE, "DS_ONLINE", \ DS_OFFLINE, "DS_OFFLINE", \ DS_ONLINE_CONTAINERS, "DS_ONLINE_CONTAINERS", \ DS_FSAPRINT, "DS_FSAPRINT", \ CL_CFG_SET_HOST_IDS, "CL_CFG_SET_HOST_IDS", \ CL_CFG_SET_PARTNER_HOST_IDS, "CL_CFG_SET_PARTNER_HOST_IDS", \ CL_CFG_GET_CLUSTER_CONFIG, "CL_CFG_GET_CLUSTER_CONFIG", \ CC_CLI_CLEAR_MESSAGE_BUFFER, "CC_CLI_CLEAR_MESSAGE_BUFFER", \ CC_SRV_CLEAR_MESSAGE_BUFFER, "CC_SRV_CLEAR_MESSAGE_BUFFER", \ CC_CLI_SHOW_MESSAGE_BUFFER, "CC_CLI_SHOW_MESSAGE_BUFFER", \ CC_SRV_SHOW_MESSAGE_BUFFER, "CC_SRV_SHOW_MESSAGE_BUFFER", \ CC_CLI_SEND_MESSAGE, "CC_CLI_SEND_MESSAGE", \ CC_SRV_SEND_MESSAGE, "CC_SRV_SEND_MESSAGE", \ CC_CLI_GET_MESSAGE, "CC_CLI_GET_MESSAGE", \ CC_SRV_GET_MESSAGE, "CC_SRV_GET_MESSAGE", \ CC_SEND_TEST_MESSAGE, "CC_SEND_TEST_MESSAGE", \ CC_GET_BUSINFO, "CC_GET_BUSINFO", \ CC_GET_PORTINFO, "CC_GET_PORTINFO", \ CC_GET_NAMEINFO, "CC_GET_NAMEINFO", \ CC_GET_CONFIGINFO, "CC_GET_CONFIGINFO", \ CQ_QUORUM_OP, "CQ_QUORUM_OP" #define AAC_AIF_SUBCMD_KEY_STRINGS \ AifCmdEventNotify, "AifCmdEventNotify", \ AifCmdJobProgress, "AifCmdJobProgress", \ AifCmdAPIReport, "AifCmdAPIReport", \ AifCmdDriverNotify, "AifCmdDriverNotify", \ AifReqJobList, "AifReqJobList", \ AifReqJobsForCtr, "AifReqJobsForCtr", \ AifReqJobsForScsi, "AifReqJobsForScsi", \ AifReqJobReport, "AifReqJobReport", \ AifReqTerminateJob, "AifReqTerminateJob", \ AifReqSuspendJob, "AifReqSuspendJob", \ AifReqResumeJob, "AifReqResumeJob", \ AifReqSendAPIReport, "AifReqSendAPIReport", \ AifReqAPIJobStart, "AifReqAPIJobStart", \ AifReqAPIJobUpdate, "AifReqAPIJobUpdate", \ AifReqAPIJobFinish, "AifReqAPIJobFinish" #define AAC_IOCTL_SUBCMD_KEY_STRINGS \ Reserved_IOCTL, "Reserved_IOCTL", \ GetDeviceHandle, "GetDeviceHandle", \ BusTargetLun_to_DeviceHandle, "BusTargetLun_to_DeviceHandle", \ DeviceHandle_to_BusTargetLun, "DeviceHandle_to_BusTargetLun", \ RescanBus, "RescanBus", \ GetDeviceProbeInfo, "GetDeviceProbeInfo", \ GetDeviceCapacity, "GetDeviceCapacity", \ GetContainerProbeInfo, "GetContainerProbeInfo", \ GetRequestedMemorySize, "GetRequestedMemorySize", \ GetBusInfo, "GetBusInfo", \ GetVendorSpecific, "GetVendorSpecific", \ EnhancedGetDeviceProbeInfo, "EnhancedGetDeviceProbeInfo", \ EnhancedGetBusInfo, "EnhancedGetBusInfo", \ SetupExtendedCounters, "SetupExtendedCounters", \ GetPerformanceCounters, "GetPerformanceCounters", \ ResetPerformanceCounters, "ResetPerformanceCounters", \ ReadModePage, "ReadModePage", \ WriteModePage, "WriteModePage", \ ReadDriveParameter, "ReadDriveParameter", \ WriteDriveParameter, "WriteDriveParameter", \ ResetAdapter, "ResetAdapter", \ ResetBus, "ResetBus", \ ResetBusDevice, "ResetBusDevice", \ ExecuteSrb, "ExecuteSrb", \ Create_IO_Task, "Create_IO_Task", \ Delete_IO_Task, "Delete_IO_Task", \ Get_IO_Task_Info, "Get_IO_Task_Info", \ Check_Task_Progress, "Check_Task_Progress", \ InjectError, "InjectError", \ GetDeviceDefectCounts, "GetDeviceDefectCounts", \ GetDeviceDefectInfo, "GetDeviceDefectInfo", \ GetDeviceStatus, "GetDeviceStatus", \ ClearDeviceStatus, "ClearDeviceStatus", \ DiskSpinControl, "DiskSpinControl", \ DiskSmartControl, "DiskSmartControl", \ WriteSame, "WriteSame", \ ReadWriteLong, "ReadWriteLong", \ FormatUnit, "FormatUnit", \ TargetDeviceControl, "TargetDeviceControl", \ TargetChannelControl, "TargetChannelControl", \ FlashNewCode, "FlashNewCode", \ DiskCheck, "DiskCheck", \ RequestSense, "RequestSense", \ DiskPERControl, "DiskPERControl", \ Read10, "Read10", \ Write10, "Write10" #define AAC_AIFEN_KEY_STRINGS \ AifEnGeneric, "Generic", \ AifEnTaskComplete, "TaskComplete", \ AifEnConfigChange, "Config change", \ AifEnContainerChange, "Container change", \ AifEnDeviceFailure, "device failed", \ AifEnMirrorFailover, "Mirror failover", \ AifEnContainerEvent, "container event", \ AifEnFileSystemChange, "File system changed", \ AifEnConfigPause, "Container pause event", \ AifEnConfigResume, "Container resume event", \ AifEnFailoverChange, "Failover space assignment changed", \ AifEnRAID5RebuildDone, "RAID5 rebuild finished", \ AifEnEnclosureManagement, "Enclosure management event", \ AifEnBatteryEvent, "battery event", \ AifEnAddContainer, "Add container", \ AifEnDeleteContainer, "Delete container", \ AifEnSMARTEvent, "SMART Event", \ AifEnBatteryNeedsRecond, "battery needs reconditioning", \ AifEnClusterEvent, "cluster event", \ AifEnDiskSetEvent, "disk set event occured", \ AifDenMorphComplete, "morph operation completed", \ AifDenVolumeExtendComplete, "VolumeExtendComplete" struct aac_key_strings { int key; char *message; }; extern struct scsi_key_strings scsi_cmds[]; static struct aac_key_strings aac_fib_cmds[] = { AAC_FIB_CMD_KEY_STRINGS, -1, NULL }; static struct aac_key_strings aac_ctvm_subcmds[] = { AAC_CTVM_SUBCMD_KEY_STRINGS, -1, NULL }; static struct aac_key_strings aac_ct_subcmds[] = { AAC_CT_SUBCMD_KEY_STRINGS, -1, NULL }; static struct aac_key_strings aac_cl_subcmds[] = { AAC_CL_SUBCMD_KEY_STRINGS, -1, NULL }; static struct aac_key_strings aac_aif_subcmds[] = { AAC_AIF_SUBCMD_KEY_STRINGS, -1, NULL }; static struct aac_key_strings aac_ioctl_subcmds[] = { AAC_IOCTL_SUBCMD_KEY_STRINGS, -1, NULL }; static struct aac_key_strings aac_aifens[] = { AAC_AIFEN_KEY_STRINGS, -1, NULL }; /* * The following function comes from Adaptec: * * Get the firmware print buffer parameters from the firmware, * if the command was successful map in the address. */ static int aac_get_fw_debug_buffer(struct aac_softstate *softs) { if (aac_sync_mbcommand(softs, AAC_MONKER_GETDRVPROP, 0, 0, 0, 0, NULL) == AACOK) { uint32_t mondrv_buf_paddrl = AAC_MAILBOX_GET(softs, 1); uint32_t mondrv_buf_paddrh = AAC_MAILBOX_GET(softs, 2); uint32_t mondrv_buf_size = AAC_MAILBOX_GET(softs, 3); uint32_t mondrv_hdr_size = AAC_MAILBOX_GET(softs, 4); if (mondrv_buf_size) { uint32_t offset = mondrv_buf_paddrl - \ softs->pci_mem_base_paddr; /* * See if the address is already mapped in, and * if so set it up from the base address */ if ((mondrv_buf_paddrh == 0) && (offset + mondrv_buf_size < softs->map_size)) { mutex_enter(&aac_prt_mutex); softs->debug_buf_offset = offset; softs->debug_header_size = mondrv_hdr_size; softs->debug_buf_size = mondrv_buf_size; softs->debug_fw_flags = 0; softs->debug_flags &= ~AACDB_FLAGS_FW_PRINT; mutex_exit(&aac_prt_mutex); return (AACOK); } } } return (AACERR); } int aac_dbflag_on(struct aac_softstate *softs, int flag) { int debug_flags = softs ? softs->debug_flags : aac_debug_flags; return ((debug_flags & (AACDB_FLAGS_FW_PRINT | \ AACDB_FLAGS_KERNEL_PRINT)) && (debug_flags & flag)); } static void aac_cmn_err(struct aac_softstate *softs, uint_t lev, char sl, int noheader) { if (noheader) { if (sl) { aac_fmt[0] = sl; cmn_err(lev, aac_fmt, aac_prt_buf); } else { cmn_err(lev, &aac_fmt[1], aac_prt_buf); } } else { if (sl) { aac_fmt_header[0] = sl; cmn_err(lev, aac_fmt_header, softs->vendor_name, softs->instance, aac_prt_buf); } else { cmn_err(lev, &aac_fmt_header[1], softs->vendor_name, softs->instance, aac_prt_buf); } } } /* * The following function comes from Adaptec: * * Format and print out the data passed in to UART or console * as specified by debug flags. */ void aac_printf(struct aac_softstate *softs, uint_t lev, const char *fmt, ...) { va_list args; char sl; /* system log character */ mutex_enter(&aac_prt_mutex); /* Set up parameters and call sprintf function to format the data */ if (strchr("^!?", fmt[0]) == NULL) { sl = 0; } else { sl = fmt[0]; fmt++; } va_start(args, fmt); (void) vsprintf(aac_prt_buf, fmt, args); va_end(args); /* Make sure the softs structure has been passed in for this section */ if (softs) { if ((softs->debug_flags & AACDB_FLAGS_FW_PRINT) && /* If we are set up for a Firmware print */ (softs->debug_buf_size)) { uint32_t count, i; /* Make sure the string size is within boundaries */ count = strlen(aac_prt_buf); if (count > softs->debug_buf_size) count = (uint16_t)softs->debug_buf_size; /* * Wait for no more than AAC_PRINT_TIMEOUT for the * previous message length to clear (the handshake). */ for (i = 0; i < AAC_PRINT_TIMEOUT; i++) { if (!PCI_MEM_GET32(softs, softs->debug_buf_offset + \ AAC_FW_DBG_STRLEN_OFFSET)) break; drv_usecwait(1000); } /* * If the length is clear, copy over the message, the * flags, and the length. Make sure the length is the * last because that is the signal for the Firmware to * pick it up. */ if (!PCI_MEM_GET32(softs, softs->debug_buf_offset + \ AAC_FW_DBG_STRLEN_OFFSET)) { PCI_MEM_REP_PUT8(softs, softs->debug_buf_offset + \ softs->debug_header_size, aac_prt_buf, count); PCI_MEM_PUT32(softs, softs->debug_buf_offset + \ AAC_FW_DBG_FLAGS_OFFSET, softs->debug_fw_flags); PCI_MEM_PUT32(softs, softs->debug_buf_offset + \ AAC_FW_DBG_STRLEN_OFFSET, count); } else { cmn_err(CE_WARN, "UART output fail"); softs->debug_flags &= ~AACDB_FLAGS_FW_PRINT; } } /* * If the Kernel Debug Print flag is set, send it off * to the Kernel Debugger */ if (softs->debug_flags & AACDB_FLAGS_KERNEL_PRINT) aac_cmn_err(softs, lev, sl, (softs->debug_flags & AACDB_FLAGS_NO_HEADERS)); } else { /* Driver not initialized yet, no firmware or header output */ if (aac_debug_flags & AACDB_FLAGS_KERNEL_PRINT) aac_cmn_err(softs, lev, sl, 1); } mutex_exit(&aac_prt_mutex); } /* * Translate command number to description string */ static char * aac_cmd_name(int cmd, struct aac_key_strings *cmdlist) { int i; for (i = 0; cmdlist[i].key != -1; i++) { if (cmd == cmdlist[i].key) return (cmdlist[i].message); } return (NULL); } static void aac_print_scmd(struct aac_softstate *softs, struct aac_cmd *acp) { struct scsi_pkt *pkt = acp->pkt; struct scsi_address *ap = &pkt->pkt_address; int is_pd = 0; int ctl = ddi_get_instance(softs->devinfo_p); int tgt = ap->a_target; int lun = ap->a_lun; union scsi_cdb *cdbp = (void *)pkt->pkt_cdbp; uchar_t cmd = cdbp->scc_cmd; char *desc; if (tgt >= AAC_MAX_LD) { is_pd = 1; ctl = ((struct aac_nondasd *)acp->dvp)->bus; tgt = ((struct aac_nondasd *)acp->dvp)->tid; lun = 0; } if ((desc = aac_cmd_name(cmd, (struct aac_key_strings *)scsi_cmds)) == NULL) { aac_printf(softs, CE_NOTE, "SCMD> Unknown(0x%2x) --> c%dt%dL%d %s", cmd, ctl, tgt, lun, is_pd ? "(pd)" : ""); return; } switch (cmd) { case SCMD_READ: case SCMD_WRITE: aac_printf(softs, CE_NOTE, "SCMD> %s 0x%x[%d] %s --> c%dt%dL%d %s", desc, GETG0ADDR(cdbp), GETG0COUNT(cdbp), (acp->flags & AAC_CMD_NO_INTR) ? "poll" : "intr", ctl, tgt, lun, is_pd ? "(pd)" : ""); break; case SCMD_READ_G1: case SCMD_WRITE_G1: aac_printf(softs, CE_NOTE, "SCMD> %s 0x%x[%d] %s --> c%dt%dL%d %s", desc, GETG1ADDR(cdbp), GETG1COUNT(cdbp), (acp->flags & AAC_CMD_NO_INTR) ? "poll" : "intr", ctl, tgt, lun, is_pd ? "(pd)" : ""); break; case SCMD_READ_G4: case SCMD_WRITE_G4: aac_printf(softs, CE_NOTE, "SCMD> %s 0x%x.%08x[%d] %s --> c%dt%dL%d %s", desc, GETG4ADDR(cdbp), GETG4ADDRTL(cdbp), GETG4COUNT(cdbp), (acp->flags & AAC_CMD_NO_INTR) ? "poll" : "intr", ctl, tgt, lun, is_pd ? "(pd)" : ""); break; case SCMD_READ_G5: case SCMD_WRITE_G5: aac_printf(softs, CE_NOTE, "SCMD> %s 0x%x[%d] %s --> c%dt%dL%d %s", desc, GETG5ADDR(cdbp), GETG5COUNT(cdbp), (acp->flags & AAC_CMD_NO_INTR) ? "poll" : "intr", ctl, tgt, lun, is_pd ? "(pd)" : ""); break; default: aac_printf(softs, CE_NOTE, "SCMD> %s --> c%dt%dL%d %s", desc, ctl, tgt, lun, is_pd ? "(pd)" : ""); } } void aac_print_fib(struct aac_softstate *softs, struct aac_slot *slotp) { struct aac_cmd *acp = slotp->acp; struct aac_fib *fibp = slotp->fibp; ddi_acc_handle_t acc = slotp->fib_acc_handle; uint16_t fib_size; uint32_t fib_cmd, sub_cmd; char *cmdstr, *subcmdstr; char *caller; int i; if (acp) { if (!(softs->debug_fib_flags & acp->fib_flags)) return; if (acp->fib_flags & AACDB_FLAGS_FIB_SCMD) caller = "SCMD"; else if (acp->fib_flags & AACDB_FLAGS_FIB_IOCTL) caller = "IOCTL"; else if (acp->fib_flags & AACDB_FLAGS_FIB_SRB) caller = "SRB"; else return; } else { if (!(softs->debug_fib_flags & AACDB_FLAGS_FIB_SYNC)) return; caller = "SYNC"; } fib_cmd = ddi_get16(acc, &fibp->Header.Command); cmdstr = aac_cmd_name(fib_cmd, aac_fib_cmds); sub_cmd = (uint32_t)-1; subcmdstr = NULL; /* Print FIB header */ if (softs->debug_fib_flags & AACDB_FLAGS_FIB_HEADER) { aac_printf(softs, CE_NOTE, "FIB> from %s", caller); aac_printf(softs, CE_NOTE, " XferState %d", ddi_get32(acc, &fibp->Header.XferState)); aac_printf(softs, CE_NOTE, " Command %d", ddi_get16(acc, &fibp->Header.Command)); aac_printf(softs, CE_NOTE, " StructType %d", ddi_get8(acc, &fibp->Header.StructType)); aac_printf(softs, CE_NOTE, " Flags 0x%x", ddi_get8(acc, &fibp->Header.Flags)); aac_printf(softs, CE_NOTE, " Size %d", ddi_get16(acc, &fibp->Header.Size)); aac_printf(softs, CE_NOTE, " SenderSize %d", ddi_get16(acc, &fibp->Header.SenderSize)); aac_printf(softs, CE_NOTE, " SenderAddr 0x%x", ddi_get32(acc, &fibp->Header.SenderFibAddress)); aac_printf(softs, CE_NOTE, " RcvrAddr 0x%x", ddi_get32(acc, &fibp->Header.ReceiverFibAddress)); aac_printf(softs, CE_NOTE, " SenderData 0x%x", ddi_get32(acc, &fibp->Header.SenderData)); } /* Print FIB data */ switch (fib_cmd) { case ContainerCommand: sub_cmd = ddi_get32(acc, (void *)&(((uint32_t *)(void *)&fibp->data[0])[0])); subcmdstr = aac_cmd_name(sub_cmd, aac_ctvm_subcmds); if (subcmdstr == NULL) break; switch (sub_cmd) { case VM_ContainerConfig: { struct aac_Container *pContainer = (struct aac_Container *)fibp->data; fib_cmd = sub_cmd; cmdstr = subcmdstr; sub_cmd = (uint32_t)-1; subcmdstr = NULL; sub_cmd = ddi_get32(acc, &pContainer->CTCommand.command); subcmdstr = aac_cmd_name(sub_cmd, aac_ct_subcmds); if (subcmdstr == NULL) break; aac_printf(softs, CE_NOTE, "FIB> %s (0x%x, 0x%x, 0x%x)", subcmdstr, ddi_get32(acc, &pContainer->CTCommand.param[0]), ddi_get32(acc, &pContainer->CTCommand.param[1]), ddi_get32(acc, &pContainer->CTCommand.param[2])); return; } case VM_Ioctl: fib_cmd = sub_cmd; cmdstr = subcmdstr; sub_cmd = (uint32_t)-1; subcmdstr = NULL; sub_cmd = ddi_get32(acc, (void *)&(((uint32_t *)(void *)&fibp->data[0])[4])); subcmdstr = aac_cmd_name(sub_cmd, aac_ioctl_subcmds); break; case VM_CtBlockRead: case VM_CtBlockWrite: { struct aac_blockread *br = (struct aac_blockread *)fibp->data; struct aac_sg_table *sg = &br->SgMap; uint32_t sgcount = ddi_get32(acc, &sg->SgCount); aac_printf(softs, CE_NOTE, "FIB> %s Container %d 0x%x/%d", subcmdstr, ddi_get32(acc, &br->ContainerId), ddi_get32(acc, &br->BlockNumber), ddi_get32(acc, &br->ByteCount)); for (i = 0; i < sgcount; i++) aac_printf(softs, CE_NOTE, " %d: 0x%08x/%d", i, ddi_get32(acc, &sg->SgEntry[i].SgAddress), ddi_get32(acc, &sg->SgEntry[i]. \ SgByteCount)); return; } } break; case ContainerCommand64: { struct aac_blockread64 *br = (struct aac_blockread64 *)fibp->data; struct aac_sg_table64 *sg = &br->SgMap64; uint32_t sgcount = ddi_get32(acc, &sg->SgCount); uint64_t sgaddr; sub_cmd = br->Command; subcmdstr = NULL; if (sub_cmd == VM_CtHostRead64) subcmdstr = "VM_CtHostRead64"; else if (sub_cmd == VM_CtHostWrite64) subcmdstr = "VM_CtHostWrite64"; else break; aac_printf(softs, CE_NOTE, "FIB> %s Container %d 0x%x/%d", subcmdstr, ddi_get16(acc, &br->ContainerId), ddi_get32(acc, &br->BlockNumber), ddi_get16(acc, &br->SectorCount)); for (i = 0; i < sgcount; i++) { sgaddr = ddi_get64(acc, &sg->SgEntry64[i].SgAddress); aac_printf(softs, CE_NOTE, " %d: 0x%08x.%08x/%d", i, AAC_MS32(sgaddr), AAC_LS32(sgaddr), ddi_get32(acc, &sg->SgEntry64[i]. \ SgByteCount)); } return; } case RawIo: { struct aac_raw_io *io = (struct aac_raw_io *)fibp->data; struct aac_sg_tableraw *sg = &io->SgMapRaw; uint32_t sgcount = ddi_get32(acc, &sg->SgCount); uint64_t sgaddr; aac_printf(softs, CE_NOTE, "FIB> RawIo Container %d 0x%llx/%d 0x%x", ddi_get16(acc, &io->ContainerId), ddi_get64(acc, &io->BlockNumber), ddi_get32(acc, &io->ByteCount), ddi_get16(acc, &io->Flags)); for (i = 0; i < sgcount; i++) { sgaddr = ddi_get64(acc, &sg->SgEntryRaw[i].SgAddress); aac_printf(softs, CE_NOTE, " %d: 0x%08x.%08x/%d", i, AAC_MS32(sgaddr), AAC_LS32(sgaddr), ddi_get32(acc, &sg->SgEntryRaw[i].SgByteCount)); } return; } case ClusterCommand: sub_cmd = ddi_get32(acc, (void *)&(((uint32_t *)(void *)fibp->data)[0])); subcmdstr = aac_cmd_name(sub_cmd, aac_cl_subcmds); break; case AifRequest: sub_cmd = ddi_get32(acc, (void *)&(((uint32_t *)(void *)fibp->data)[0])); subcmdstr = aac_cmd_name(sub_cmd, aac_aif_subcmds); break; default: break; } fib_size = ddi_get16(acc, &(fibp->Header.Size)); if (subcmdstr) aac_printf(softs, CE_NOTE, "FIB> %s, sz=%d", subcmdstr, fib_size); else if (cmdstr && sub_cmd == (uint32_t)-1) aac_printf(softs, CE_NOTE, "FIB> %s, sz=%d", cmdstr, fib_size); else if (cmdstr) aac_printf(softs, CE_NOTE, "FIB> %s: Unknown(0x%x), sz=%d", cmdstr, sub_cmd, fib_size); else aac_printf(softs, CE_NOTE, "FIB> Unknown(0x%x), sz=%d", fib_cmd, fib_size); } static void aac_print_aif(struct aac_softstate *softs, struct aac_aif_command *aif) { int aif_command; uint32_t aif_seqnumber; int aif_en_type; char *str; aif_command = LE_32(aif->command); aif_seqnumber = LE_32(aif->seqNumber); aif_en_type = LE_32(aif->data.EN.type); switch (aif_command) { case AifCmdEventNotify: str = aac_cmd_name(aif_en_type, aac_aifens); if (str) aac_printf(softs, CE_NOTE, "AIF! %s", str); else aac_printf(softs, CE_NOTE, "AIF! Unknown(0x%x)", aif_en_type); break; case AifCmdJobProgress: switch (LE_32(aif->data.PR[0].status)) { case AifJobStsSuccess: str = "success"; break; case AifJobStsFinished: str = "finished"; break; case AifJobStsAborted: str = "aborted"; break; case AifJobStsFailed: str = "failed"; break; case AifJobStsSuspended: str = "suspended"; break; case AifJobStsRunning: str = "running"; break; default: str = "unknown"; break; } aac_printf(softs, CE_NOTE, "AIF! JobProgress (%d) - %s (%d, %d)", aif_seqnumber, str, LE_32(aif->data.PR[0].currentTick), LE_32(aif->data.PR[0].finalTick)); break; case AifCmdAPIReport: aac_printf(softs, CE_NOTE, "AIF! APIReport (%d)", aif_seqnumber); break; case AifCmdDriverNotify: aac_printf(softs, CE_NOTE, "AIF! DriverNotify (%d)", aif_seqnumber); break; default: aac_printf(softs, CE_NOTE, "AIF! AIF %d (%d)", aif_command, aif_seqnumber); break; } } #endif /* DEBUG */