/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at
* http://www.opensource.org/licenses/cddl1.txt.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2004-2012 Emulex. All rights reserved.
* Use is subject to license terms.
*/
#include <emlxs.h>
/* Required for EMLXS_CONTEXT in EMLXS_MSGF calls */
EMLXS_MSG_DEF(EMLXS_DIAG_C);
uint32_t emlxs_diag_pattern[256] = {
/* Walking ones */
0x80000000, 0x40000000, 0x20000000, 0x10000000,
0x08000000, 0x04000000, 0x02000000, 0x01000000,
0x00800000, 0x00400000, 0x00200000, 0x00100000,
0x00080000, 0x00040000, 0x00020000, 0x00010000,
0x00008000, 0x00004000, 0x00002000, 0x00001000,
0x00000800, 0x00000400, 0x00000200, 0x00000100,
0x00000080, 0x00000040, 0x00000020, 0x00000010,
0x00000008, 0x00000004, 0x00000002, 0x00000001,
/* Walking zeros */
0x7fffffff, 0xbfffffff, 0xdfffffff, 0xefffffff,
0xf7ffffff, 0xfbffffff, 0xfdffffff, 0xfeffffff,
0xff7fffff, 0xffbfffff, 0xffdfffff, 0xffefffff,
0xfff7ffff, 0xfffbffff, 0xfffdffff, 0xfffeffff,
0xffff7fff, 0xffffbfff, 0xffffdfff, 0xffffefff,
0xfffff7ff, 0xfffffbff, 0xfffffdff, 0xfffffeff,
0xffffff7f, 0xffffffbf, 0xffffffdf, 0xffffffef,
0xfffffff7, 0xfffffffb, 0xfffffffd, 0xfffffffe,
/* all zeros */
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
/* all ones */
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
/* all 5's */
0x55555555, 0x55555555, 0x55555555, 0x55555555,
0x55555555, 0x55555555, 0x55555555, 0x55555555,
0x55555555, 0x55555555, 0x55555555, 0x55555555,
0x55555555, 0x55555555, 0x55555555, 0x55555555,
0x55555555, 0x55555555, 0x55555555, 0x55555555,
0x55555555, 0x55555555, 0x55555555, 0x55555555,
0x55555555, 0x55555555, 0x55555555, 0x55555555,
0x55555555, 0x55555555, 0x55555555, 0x55555555,
/* all a's */
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
/* all 5a's */
0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a,
0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a,
0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a,
0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a,
0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a,
0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a,
0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a,
0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a,
/* all a5's */
0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5,
0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5,
0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5,
0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5,
0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5,
0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5,
0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5,
0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5
};
/* Default pkt callback routine */
static void
emlxs_diag_pkt_callback(fc_packet_t *pkt)
{
emlxs_port_t *port = (emlxs_port_t *)pkt->pkt_ulp_private;
/* Set the completed flag and wake up sleeping threads */
mutex_enter(&EMLXS_PKT_LOCK);
pkt->pkt_tran_flags |= FC_TRAN_COMPLETED;
cv_broadcast(&EMLXS_PKT_CV);
mutex_exit(&EMLXS_PKT_LOCK);
return;
} /* emlxs_diag_pkt_callback() */
extern uint32_t
emlxs_diag_echo_run(emlxs_port_t *port, uint32_t did, uint32_t pattern)
{
emlxs_hba_t *hba = HBA;
uint32_t i = 0;
uint32_t rval = FC_SUCCESS;
int32_t pkt_ret;
fc_packet_t *pkt;
ELS_PKT *els;
clock_t timeout;
uint8_t *pkt_resp;
char *pattern_buffer;
uint32_t length;
uint32_t *lptr;
NODELIST *ndlp;
uint8_t *pat;
/* Check did */
if (did == 0) {
did = port->did;
}
/* Check if device is ready */
if ((hba->state < FC_LINK_UP) || (port->did == 0)) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_diag_error_msg,
"ECHO: HBA not ready.");
return (FC_TRAN_BUSY);
}
/* Check for the host node */
ndlp = emlxs_node_find_did(port, port->did, 1);
if (!ndlp || !ndlp->nlp_active) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_diag_error_msg,
"ECHO: HBA not ready.");
return (FC_TRAN_BUSY);
}
length = 124;
/* Prepare ECHO pkt */
if (!(pkt = emlxs_pkt_alloc(port, sizeof (uint32_t) + length,
sizeof (uint32_t) + length, 0, KM_NOSLEEP))) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_diag_error_msg,
"ECHO: Unable to allocate packet. size=%x",
sizeof (uint32_t) + length);
return (FC_NOMEM);
}
/* pkt initialization */
pkt->pkt_tran_type = FC_PKT_EXCHANGE;
pkt->pkt_timeout = 60;
/* Build the fc header */
pkt->pkt_cmd_fhdr.d_id = did;
pkt->pkt_cmd_fhdr.r_ctl = R_CTL_EXTENDED_SVC | R_CTL_UNSOL_CONTROL;
pkt->pkt_cmd_fhdr.s_id = port->did;
pkt->pkt_cmd_fhdr.type = FC_TYPE_EXTENDED_LS;
pkt->pkt_cmd_fhdr.f_ctl =
F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE | F_CTL_END_SEQ;
pkt->pkt_cmd_fhdr.seq_id = 0;
pkt->pkt_cmd_fhdr.df_ctl = 0;
pkt->pkt_cmd_fhdr.seq_cnt = 0;
pkt->pkt_cmd_fhdr.ox_id = 0xffff;
pkt->pkt_cmd_fhdr.rx_id = 0xffff;
pkt->pkt_cmd_fhdr.ro = 0;
pkt->pkt_comp = emlxs_diag_pkt_callback;
/* Build the command */
els = (ELS_PKT *) pkt->pkt_cmd;
els->elsCode = 0x10;
pattern_buffer = (char *)els->un.pad;
if (pattern) {
/* Fill the transmit buffer with the pattern */
lptr = (uint32_t *)pattern_buffer;
for (i = 0; i < length; i += 4) {
*lptr++ = pattern;
}
} else {
/* Program the default echo pattern */
bzero(pattern_buffer, length);
(void) snprintf(pattern_buffer, length,
"Emulex. We network storage. Emulex. We network storage. "
"Emulex. We network storage. Emulex. We network storage.");
}
/* Send ECHO pkt */
if ((rval = emlxs_pkt_send(pkt, 1)) != FC_SUCCESS) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_diag_error_msg,
"ECHO: Packet send failed.");
goto done;
}
/* Wait for ECHO completion */
mutex_enter(&EMLXS_PKT_LOCK);
timeout = emlxs_timeout(hba, (pkt->pkt_timeout + 15));
pkt_ret = 0;
while ((pkt_ret != -1) && !(pkt->pkt_tran_flags & FC_TRAN_COMPLETED)) {
pkt_ret =
cv_timedwait(&EMLXS_PKT_CV, &EMLXS_PKT_LOCK, timeout);
}
mutex_exit(&EMLXS_PKT_LOCK);
if (pkt_ret == -1) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_echo_failed_msg,
"Packet timed out.");
return (FC_ABORTED);
}
if (pkt->pkt_state != FC_PKT_SUCCESS) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_echo_failed_msg,
"Transport error.");
rval = FC_TRANSPORT_ERROR;
goto done;
}
/* Check response payload */
pkt_resp = (uint8_t *)pkt->pkt_resp + 4;
pat = (uint8_t *)pattern_buffer;
rval = FC_SUCCESS;
for (i = 0; i < length; i++, pkt_resp++, pat++) {
if (*pkt_resp != *pat) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_echo_failed_msg,
"Data miscompare. did=%06x length=%d. Offset %d "
"value %02x should be %02x.", did, length, i,
*pkt_resp, *pat);
rval = EMLXS_TEST_FAILED;
break;
}
}
if (rval == FC_SUCCESS) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_echo_complete_msg,
"did=%06x length=%d pattern=%02x,%02x,%02x,%02x...",
did, length, pattern_buffer[0] & 0xff,
pattern_buffer[1] & 0xff, pattern_buffer[2] & 0xff,
pattern_buffer[3] & 0xff);
}
done:
/* Free the echo pkt */
emlxs_pkt_free(pkt);
return (rval);
} /* emlxs_diag_echo_run() */
extern uint32_t
emlxs_diag_biu_run(emlxs_hba_t *hba, uint32_t pattern)
{
emlxs_port_t *port = &PPORT;
MAILBOXQ *mbq = NULL;
MATCHMAP *mp = NULL;
MATCHMAP *mp1 = NULL;
uint32_t i;
uint8_t *inptr;
uint8_t *outptr;
int32_t rval = FC_SUCCESS;
uint32_t *lptr;
/* Check if device is ready */
if (hba->state < FC_LINK_DOWN) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_diag_error_msg,
"BIU: HBA not ready.");
return (FC_TRAN_BUSY);
}
/*
* Get a buffer which will be used for the mailbox command
*/
if ((mbq = (MAILBOXQ *) emlxs_mem_get(hba, MEM_MBOX)) == 0) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_diag_error_msg,
"BIU: Mailbox allocation failed.");
rval = FC_NOMEM;
goto done;
}
/*
* Setup and issue mailbox RUN BIU DIAG command Setup test buffers
*/
if (((mp = (MATCHMAP *) emlxs_mem_get(hba, MEM_BUF)) == 0) ||
((mp1 = (MATCHMAP *) emlxs_mem_get(hba, MEM_BUF)) == 0)) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_diag_error_msg,
"BIU: Buffer allocation failed.");
rval = FC_NOMEM;
goto done;
}
if (pattern) {
/* Fill the transmit buffer with the pattern */
lptr = (uint32_t *)mp->virt;
for (i = 0; i < MEM_ELSBUF_SIZE; i += 4) {
*lptr++ = pattern;
}
} else {
/* Copy the default pattern into the trasmit buffer */
bcopy((caddr_t)&emlxs_diag_pattern[0], (caddr_t)mp->virt,
MEM_ELSBUF_SIZE);
}
EMLXS_MPDATA_SYNC(mp->dma_handle, 0, MEM_ELSBUF_SIZE,
DDI_DMA_SYNC_FORDEV);
bzero(mp1->virt, MEM_ELSBUF_SIZE);
EMLXS_MPDATA_SYNC(mp1->dma_handle, 0, MEM_ELSBUF_SIZE,
DDI_DMA_SYNC_FORDEV);
/* Create the biu diag request */
(void) emlxs_mb_run_biu_diag(hba, mbq, mp->phys, mp1->phys);
rval = EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_WAIT, 60);
if (rval == MBX_TIMEOUT) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_biu_failed_msg,
"BUI diagnostic timed out.");
rval = EMLXS_TEST_FAILED;
goto done;
}
EMLXS_MPDATA_SYNC(mp1->dma_handle, 0, MEM_ELSBUF_SIZE,
DDI_DMA_SYNC_FORKERNEL);
outptr = mp->virt;
inptr = mp1->virt;
for (i = 0; i < MEM_ELSBUF_SIZE; i++, outptr++, inptr++) {
if (*outptr != *inptr) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_biu_failed_msg,
"Data miscompare. Offset %d value %02x should "
"be %02x.", i, *inptr, *outptr);
rval = EMLXS_TEST_FAILED;
goto done;
}
}
/* Wait half second before returning */
delay(drv_usectohz(500000));
rval = FC_SUCCESS;
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_biu_complete_msg, "Status Good.");
done:
if (mp) {
#ifdef FMA_SUPPORT
if (emlxs_fm_check_dma_handle(hba, mp->dma_handle)
!= DDI_FM_OK) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_invalid_dma_handle_msg,
"diag_biu_run: hdl=%p",
mp->dma_handle);
rval = EMLXS_TEST_FAILED;
}
#endif /* FMA_SUPPORT */
emlxs_mem_put(hba, MEM_BUF, (void *)mp);
}
if (mp1) {
#ifdef FMA_SUPPORT
if (emlxs_fm_check_dma_handle(hba, mp1->dma_handle)
!= DDI_FM_OK) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_invalid_dma_handle_msg,
"diag_biu_run: hdl=%p",
mp1->dma_handle);
rval = EMLXS_TEST_FAILED;
}
#endif /* FMA_SUPPORT */
emlxs_mem_put(hba, MEM_BUF, (void *)mp1);
}
if (mbq) {
emlxs_mem_put(hba, MEM_MBOX, (void *)mbq);
}
return (rval);
} /* emlxs_diag_biu_run() */
extern uint32_t
emlxs_diag_post_run(emlxs_hba_t *hba)
{
emlxs_port_t *port = &PPORT;
uint32_t rval = FC_SUCCESS;
if (hba->flag & (FC_OFFLINE_MODE | FC_OFFLINING_MODE)) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_diag_error_msg,
"POST: HBA shutdown.");
return (FC_TRAN_BUSY);
}
/* Take board offline */
if ((rval = emlxs_offline(hba, 0))) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_post_failed_msg,
"Unable to take adapter offline.");
rval = FC_RESETFAIL;
}
/* Restart the adapter */
rval = EMLXS_SLI_HBA_RESET(hba, 1, 1, 0);
switch (rval) {
case 0:
(void) emlxs_online(hba);
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_post_complete_msg,
"Status good.");
rval = FC_SUCCESS;
break;
case 1: /* failed */
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_post_failed_msg,
"HBA reset failed.");
rval = FC_RESETFAIL;
break;
case 2: /* failed */
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_diag_error_msg,
"HBA busy. Quiece and retry.");
rval = FC_STATEC_BUSY;
break;
}
return (rval);
} /* emlxs_diag_post_run() */