/* * 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 usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * 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 2009 QLogic Corporation */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "Copyright 2009 QLogic Corporation; ql_debug.c" /* * Qlogic ISP22xx/ISP23xx/ISP24xx FCA driver source * * *********************************************************************** * * ** * * NOTICE ** * * COPYRIGHT (C) 1996-2009 QLOGIC CORPORATION ** * * ALL RIGHTS RESERVED ** * * ** * *********************************************************************** * */ #include #include #include static int ql_flash_errlog_store(ql_adapter_state_t *, uint32_t *); int ql_validate_trace_desc(ql_adapter_state_t *ha); char *ql_find_trace_start(ql_adapter_state_t *ha); /* * Global Data. */ uint32_t el_message_number = 0; uint32_t ql_enable_ellock = 0; extern int getpcstack(pc_t *, int); extern char *kobj_getsymname(uintptr_t, ulong_t *); /* * ql_dump_buffer * Outputs buffer. * * Input: * string: Null terminated string (no newline at end). * buffer: buffer address. * wd_size: word size 8 bits * count: number of words. */ void ql_dump_buffer(uint8_t *b8, uint8_t wd_size, uint32_t count) { uint32_t cnt; char str[256], *sp; uint32_t *b32 = (uint32_t *)b8; uint16_t *b16 = (uint16_t *)b8; sp = &str[0]; switch (wd_size) { case 32: cmn_err(CE_CONT, " 0 4 8 C\n"); cmn_err(CE_CONT, "----------------------------------------\n"); for (cnt = 1; cnt <= count; cnt++) { (void) sprintf(sp, "%10x", *b32++); sp += 10; if (cnt % 4 == 0) { cmn_err(CE_CONT, "%s\n", str); sp = &str[0]; } } break; case 16: cmn_err(CE_CONT, " 0 2 4 6 8 A C" " E\n"); cmn_err(CE_CONT, "------------------------------------------" "------\n"); for (cnt = 1; cnt <= count; cnt++) { (void) sprintf(sp, "%6x", *b16++); sp += 6; if (cnt % 8 == 0) { cmn_err(CE_CONT, "%s\n", str); sp = &str[0]; } } break; case 8: cmn_err(CE_CONT, " 0 1 2 3 4 5 6 7 8 9 " "A B C D E F\n"); cmn_err(CE_CONT, "---------------------------------" "-------------------------------\n"); for (cnt = 1; cnt <= count; cnt++) { (void) sprintf(sp, "%4x", *b8++); sp += 4; if (cnt % 16 == 0) { cmn_err(CE_CONT, "%s\n", str); sp = &str[0]; } } break; default: break; } if (sp != &str[0]) { cmn_err(CE_CONT, "%s\n", str); } } /* * ql_el_msg * Extended logging message * * Input: * ha: adapter state pointer. * fn: function name. * ce: level * ...: Variable argument list. * * Context: * Kernel/Interrupt context. */ void ql_el_msg(ql_adapter_state_t *ha, const char *fn, int ce, ...) { uint32_t el_msg_num; char *s, *fmt = 0, *fmt1 = 0; char fmt2[256]; int rval, tmp; int tracing = 0; va_list vl; /* Tracing is the default but it can be disabled. */ if ((CFG_IST(ha, CFG_DISABLE_EXTENDED_LOGGING_TRACE) == 0) && (rval = ql_validate_trace_desc(ha) == DDI_SUCCESS)) { tracing = 1; TRACE_BUFFER_LOCK(ha); /* * Ensure enough space for the string. Wrap to * start when default message allocation size * would overrun the end. */ if ((ha->el_trace_desc->next + EL_BUFFER_RESERVE) >= ha->el_trace_desc->trace_buffer_size) { fmt = ha->el_trace_desc->trace_buffer; ha->el_trace_desc->next = 0; } else { fmt = ha->el_trace_desc->trace_buffer + ha->el_trace_desc->next; } } /* if no buffer use the stack */ if (fmt == NULL) { fmt = fmt2; } va_start(vl, ce); s = va_arg(vl, char *); if (ql_enable_ellock) { /* * Used when messages are *maybe* being lost. Adds * a unique number to the message so one can see if * any messages have been dropped. NB: This slows * down the driver, which may make the issue disappear. */ GLOBAL_EL_LOCK(); el_msg_num = ++el_message_number; GLOBAL_EL_UNLOCK(); rval = (int)snprintf(fmt, (size_t)EL_BUFFER_RESERVE, QL_BANG "QEL%d %s(%d,%d): %s, ", el_msg_num, QL_NAME, ha->instance, ha->vp_index, fn); fmt1 = fmt + rval; tmp = (int)vsnprintf(fmt1, (size_t)(uint32_t)((int)EL_BUFFER_RESERVE - rval), s, vl); rval += tmp; } else { rval = (int)snprintf(fmt, (size_t)EL_BUFFER_RESERVE, QL_BANG "QEL %s(%d,%d): %s, ", QL_NAME, ha->instance, ha->vp_index, fn); fmt1 = fmt + rval; tmp = (int)vsnprintf(fmt1, (size_t)(uint32_t)((int)EL_BUFFER_RESERVE - rval), s, vl); rval += tmp; } /* * Calculate the offset where the next message will go, * skipping the NULL. */ if (tracing) { uint16_t next = (uint16_t)(rval += 1); ha->el_trace_desc->next += next; TRACE_BUFFER_UNLOCK(ha); } if (CFG_IST(ha, CFG_ENABLE_EXTENDED_LOGGING)) { cmn_err(ce, fmt); } va_end(vl); } /* * ql_el_msg * Extended logging message * * Input: * ha: adapter state pointer. * fn: function name. * ce: level * ...: Variable argument list. * * Context: * Kernel/Interrupt context. */ void ql_dbg_msg(const char *fn, int ce, ...) { uint32_t el_msg_num; char *s; char fmt[256]; va_list vl; va_start(vl, ce); s = va_arg(vl, char *); if (ql_enable_ellock) { /* * Used when messages are *maybe* being lost. Adds * a unique number to the message to one can see if * any messages have been dropped. NB: This slows * down the driver, which may make the issue disappear. */ GLOBAL_EL_LOCK(); el_msg_num = ++el_message_number; GLOBAL_EL_UNLOCK(); (void) snprintf(fmt, EL_BUFFER_RESERVE, "QLP%d: %s %s, %s", el_msg_num, QL_NAME, fn, s); } else { (void) snprintf(fmt, EL_BUFFER_RESERVE, "QLP: %s %s, %s", QL_NAME, fn, s); } vcmn_err(ce, fmt, vl); va_end(vl); } /* * ql_stacktrace * Prints out current stack * * Input: * ha: adapter state pointer. * * Context: * Kernel/Interrupt context. */ void ql_stacktrace(ql_adapter_state_t *ha) { int depth, i; pc_t pcstack[DEBUG_STK_DEPTH]; char *sym = NULL; ulong_t off; depth = getpcstack(&pcstack[0], DEBUG_STK_DEPTH); cmn_err(CE_CONT, "%s(%d,%d): ---------- \n", QL_NAME, ha->instance, ha->vp_index); for (i = 0; i < MIN(depth, DEBUG_STK_DEPTH); i++) { sym = kobj_getsymname((uintptr_t)pcstack[i], &off); if (sym == NULL) { cmn_err(CE_CONT, "%s(%d,%d): sym is NULL\n", QL_NAME, ha->instance, ha->vp_index); } else { cmn_err(CE_CONT, "%s(%d,%d): %s+%lx\n", QL_NAME, ha->instance, ha->vp_index, sym ? sym : "?", off); } } cmn_err(CE_CONT, "%s(%d,%d): ---------- \n", QL_NAME, ha->instance, ha->vp_index); } /* * ql_flash_errlog * Adds error to flash error log. * Entry Layout: * uint32_t TimeStamp; * uint16_t CodeData[4]; * * Input: * ha: adapter state pointer. * code: Error code * d1-d3: Error code data * * Returns: * ql local function return status code. * * Context: * Kernel/Interrupt context. */ int ql_flash_errlog(ql_adapter_state_t *ha, uint16_t code, uint16_t d1, uint16_t d2, uint16_t d3) { char *s; uint32_t marker[2], fdata[2], faddr; int rval; QL_PRINT_3(CE_CONT, "(%d): started\n", ha->instance); if (ha->flash_errlog_start == 0) { return (QL_NOT_SUPPORTED); } EL(ha, "code=%xh, d1=%xh, d2=%xh, d3=%xh\n", code, d1, d2, d3); /* * If marker not already found, locate or write marker. */ if (!(ha->flags & FLASH_ERRLOG_MARKER)) { /* Create marker. */ marker[0] = CHAR_TO_LONG(ha->fw_subminor_version, ha->fw_minor_version, ha->fw_major_version, 'S'); /* * Version should be of the format: YYYYMMDD-v.vv */ if ((strlen(QL_VERSION) > 9) && (QL_VERSION[8] == '-')) { s = &QL_VERSION[9]; } else { s = QL_VERSION; } for (marker[1] = 0; *s != '\0'; s++) { if (*s >= '0' && *s <= '9') { marker[1] <<= 4; marker[1] |= *s - '0'; } else if (*s != '.') { break; } } /* Locate marker. */ ha->flash_errlog_ptr = ha->flash_errlog_start; for (;;) { faddr = ha->flash_data_addr | ha->flash_errlog_ptr; (void) ql_24xx_read_flash(ha, faddr++, &fdata[0]); (void) ql_24xx_read_flash(ha, faddr++, &fdata[1]); if (fdata[0] == 0xffffffff && fdata[1] == 0xffffffff) { break; } (void) ql_24xx_read_flash(ha, faddr++, &fdata[0]); (void) ql_24xx_read_flash(ha, faddr++, &fdata[1]); ha->flash_errlog_ptr += FLASH_ERRLOG_ENTRY_SIZE; if (ha->flash_errlog_ptr >= ha->flash_errlog_start + FLASH_ERRLOG_SIZE) { EL(ha, "log full\n"); return (QL_MEMORY_FULL); } if (fdata[0] == marker[0] && fdata[1] == marker[1]) { ha->flags |= FLASH_ERRLOG_MARKER; break; } } /* No marker, write it. */ if (!(ha->flags & FLASH_ERRLOG_MARKER)) { ha->flags |= FLASH_ERRLOG_MARKER; rval = ql_flash_errlog_store(ha, marker); if (rval != QL_SUCCESS) { EL(ha, "failed marker write=%xh\n", rval); return (rval); } } } /* * Store error. */ fdata[0] = SHORT_TO_LONG(d1, code); fdata[1] = SHORT_TO_LONG(d3, d2); rval = ql_flash_errlog_store(ha, fdata); if (rval != QL_SUCCESS) { EL(ha, "failed error write=%xh\n", rval); } else { /*EMPTY*/ QL_PRINT_3(CE_CONT, "(%d): done\n", ha->instance); } return (rval); } /* * ql_flash_errlog_store * Stores error to flash. * Entry Layout: * uint32_t TimeStamp; * uint16_t CodeData[4]; * * Input: * ha: adapter state pointer. * fdata: Error code plus data. * ha->flash_errlog_ptr: Current Flash error pointer. * * Output: * ha->flash_errlog_ptr: updated pointer. * * Returns: * ql local function return status code. * * Context: * Kernel/Interrupt context. */ static int ql_flash_errlog_store(ql_adapter_state_t *ha, uint32_t *fdata) { int rval; uint64_t time; uint32_t d1, d2, faddr; QL_PRINT_3(CE_CONT, "(%d): started\n", ha->instance); /* Locate first empty entry */ for (;;) { if (ha->flash_errlog_ptr >= ha->flash_errlog_start + FLASH_ERRLOG_SIZE) { EL(ha, "log full\n"); return (QL_MEMORY_FULL); } faddr = ha->flash_data_addr | ha->flash_errlog_ptr; ha->flash_errlog_ptr += FLASH_ERRLOG_ENTRY_SIZE; (void) ql_24xx_read_flash(ha, faddr, &d1); (void) ql_24xx_read_flash(ha, faddr + 1, &d2); if (d1 == 0xffffffff && d2 == 0xffffffff) { (void) drv_getparm(TIME, &time); /* Enable flash write. */ if ((rval = ql_24xx_unprotect_flash(ha)) != QL_SUCCESS) { EL(ha, "unprotect_flash failed, rval=%xh\n", rval); return (rval); } (void) ql_24xx_write_flash(ha, faddr++, LSD(time)); (void) ql_24xx_write_flash(ha, faddr++, MSD(time)); (void) ql_24xx_write_flash(ha, faddr++, *fdata++); (void) ql_24xx_write_flash(ha, faddr++, *fdata); /* Enable flash write-protection. */ ql_24xx_protect_flash(ha); break; } } QL_PRINT_3(CE_CONT, "(%d): done\n", ha->instance); return (QL_SUCCESS); } /* * ql_dump_el_trace_buffer * Outputs extended logging trace buffer. * * Input: * ha: adapter state pointer. */ void ql_dump_el_trace_buffer(ql_adapter_state_t *ha) { char *dump_start = NULL; char *dump_current = NULL; char *trace_start; char *trace_end; int wrapped = 0; int rval; TRACE_BUFFER_LOCK(ha); rval = ql_validate_trace_desc(ha); if (rval != NULL) { cmn_err(CE_CONT, "%s(%d) Dump EL trace - invalid desc\n", QL_NAME, ha->instance); } else if ((dump_start = ql_find_trace_start(ha)) != NULL) { dump_current = dump_start; trace_start = ha->el_trace_desc->trace_buffer; trace_end = trace_start + ha->el_trace_desc->trace_buffer_size; cmn_err(CE_CONT, "%s(%d) Dump EL trace - start %p %p\n", QL_NAME, ha->instance, (void *)dump_start, (void *)trace_start); while (((uintptr_t)dump_current - (uintptr_t)trace_start) <= (uintptr_t)ha->el_trace_desc->trace_buffer_size) { /* Show it... */ cmn_err(CE_CONT, "%p - %s", (void *)dump_current, dump_current); /* Make the next the current */ dump_current += (strlen(dump_current) + 1); /* check for wrap */ if ((dump_current + EL_BUFFER_RESERVE) >= trace_end) { dump_current = trace_start; wrapped = 1; } else if (wrapped) { /* Don't go past next. */ if ((trace_start + ha->el_trace_desc->next) <= dump_current) { break; } } else if (*dump_current == NULL) { break; } } } TRACE_BUFFER_UNLOCK(ha); } /* * ql_validate_trace_desc * Ensures the extended logging trace descriptor is good * * Input: * ha: adapter state pointer. * * Returns: * ql local function return status code. */ int ql_validate_trace_desc(ql_adapter_state_t *ha) { int rval = DDI_SUCCESS; if (ha->el_trace_desc == NULL) { rval = DDI_FAILURE; } else if (ha->el_trace_desc->trace_buffer == NULL) { rval = DDI_FAILURE; } return (rval); } /* * ql_find_trace_start * Locate the oldest extended logging trace entry. * * Input: * ha: adapter state pointer. * * Returns: * Pointer to a string. * * Context: * Kernel/Interrupt context. */ char * ql_find_trace_start(ql_adapter_state_t *ha) { char *trace_start = 0; char *trace_next = 0; trace_next = ha->el_trace_desc->trace_buffer + ha->el_trace_desc->next; /* * if the buffer has not wrapped next will point at a null so * start is the beginning of the buffer. if next points at a char * then we must traverse the buffer until a null is detected and * that will be the beginning of the oldest whole object in the buffer * which is the start. */ if ((trace_next + EL_BUFFER_RESERVE) >= (ha->el_trace_desc->trace_buffer + ha->el_trace_desc->trace_buffer_size)) { trace_start = ha->el_trace_desc->trace_buffer; } else if (*trace_next != NULL) { trace_start = trace_next + (strlen(trace_next) + 1); } else { trace_start = ha->el_trace_desc->trace_buffer; } return (trace_start); }