1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /* Copyright 2009 QLogic Corporation */
23 
24 /*
25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 #pragma ident	"Copyright 2009 QLogic Corporation; ql_debug.c"
30 
31 /*
32  * Qlogic ISP22xx/ISP23xx/ISP24xx FCA driver source
33  *
34  * ***********************************************************************
35  * *									**
36  * *				NOTICE					**
37  * *		COPYRIGHT (C) 1996-2009 QLOGIC CORPORATION		**
38  * *			ALL RIGHTS RESERVED				**
39  * *									**
40  * ***********************************************************************
41  *
42  */
43 
44 #include <ql_apps.h>
45 #include <ql_api.h>
46 #include <ql_debug.h>
47 
48 static int ql_flash_errlog_store(ql_adapter_state_t *, uint32_t *);
49 int ql_validate_trace_desc(ql_adapter_state_t *ha);
50 char *ql_find_trace_start(ql_adapter_state_t *ha);
51 
52 /*
53  * Global Data.
54  */
55 uint32_t	el_message_number = 0;
56 uint32_t	ql_enable_ellock = 0;
57 
58 extern int 	getpcstack(pc_t *, int);
59 extern char	*kobj_getsymname(uintptr_t, ulong_t *);
60 
61 /*
62  * ql_dump_buffer
63  *	 Outputs buffer.
64  *
65  * Input:
66  *	 string:	Null terminated string (no newline at end).
67  *	 buffer:	buffer address.
68  *	 wd_size:	word size 8 bits
69  *	 count:		number of words.
70  */
71 void
72 ql_dump_buffer(uint8_t *b8, uint8_t wd_size, uint32_t count)
73 {
74 	uint32_t	cnt;
75 	char		str[256], *sp;
76 	uint32_t	*b32 = (uint32_t *)b8;
77 	uint16_t	*b16 = (uint16_t *)b8;
78 
79 	sp = &str[0];
80 
81 	switch (wd_size) {
82 	case 32:
83 		cmn_err(CE_CONT, "         0         4         8         C\n");
84 		cmn_err(CE_CONT, "----------------------------------------\n");
85 
86 		for (cnt = 1; cnt <= count; cnt++) {
87 			(void) sprintf(sp, "%10x", *b32++);
88 			sp += 10;
89 			if (cnt % 4 == 0) {
90 				cmn_err(CE_CONT, "%s\n", str);
91 				sp = &str[0];
92 			}
93 		}
94 		break;
95 	case 16:
96 		cmn_err(CE_CONT, "     0     2     4     6     8     A     C"
97 		    "     E\n");
98 		cmn_err(CE_CONT, "------------------------------------------"
99 		    "------\n");
100 
101 		for (cnt = 1; cnt <= count; cnt++) {
102 			(void) sprintf(sp, "%6x", *b16++);
103 			sp += 6;
104 			if (cnt % 8 == 0) {
105 				cmn_err(CE_CONT, "%s\n", str);
106 				sp = &str[0];
107 			}
108 		}
109 		break;
110 	case 8:
111 		cmn_err(CE_CONT, "   0   1   2   3   4   5   6   7   8   9   "
112 		    "A   B   C   D   E   F\n");
113 		cmn_err(CE_CONT, "---------------------------------"
114 		    "-------------------------------\n");
115 
116 		for (cnt = 1; cnt <= count; cnt++) {
117 			(void) sprintf(sp, "%4x", *b8++);
118 			sp += 4;
119 			if (cnt % 16 == 0) {
120 				cmn_err(CE_CONT, "%s\n", str);
121 				sp = &str[0];
122 			}
123 		}
124 		break;
125 	default:
126 		break;
127 	}
128 	if (sp != &str[0]) {
129 		cmn_err(CE_CONT, "%s\n", str);
130 	}
131 }
132 
133 /*
134  * ql_el_msg
135  *	Extended logging message
136  *
137  * Input:
138  *	ha:	adapter state pointer.
139  *	fn:	function name.
140  *	ce:	level
141  *	...:	Variable argument list.
142  *
143  * Context:
144  *	Kernel/Interrupt context.
145  */
146 void
147 ql_el_msg(ql_adapter_state_t *ha, const char *fn, int ce, ...)
148 {
149 	uint32_t	el_msg_num;
150 	char		*s, *fmt = 0, *fmt1 = 0;
151 	char		fmt2[256];
152 	int		rval, tmp;
153 	int		tracing = 0;
154 	va_list		vl;
155 
156 	/* Tracing is the default but it can be disabled. */
157 	if (((ha->cfg_flags & CFG_DISABLE_EXTENDED_LOGGING_TRACE) == 0) &&
158 	    (rval = ql_validate_trace_desc(ha) == DDI_SUCCESS)) {
159 		tracing = 1;
160 
161 		TRACE_BUFFER_LOCK(ha);
162 
163 		/*
164 		 * Ensure enough space for the string. Wrap to
165 		 * start when default message allocation size
166 		 * would overrun the end.
167 		 */
168 		if ((ha->el_trace_desc->next + EL_BUFFER_RESERVE) >=
169 		    ha->el_trace_desc->trace_buffer_size) {
170 			fmt = ha->el_trace_desc->trace_buffer;
171 			ha->el_trace_desc->next = 0;
172 		} else {
173 			fmt = ha->el_trace_desc->trace_buffer +
174 			    ha->el_trace_desc->next;
175 		}
176 	}
177 	/* if no buffer use the stack */
178 	if (fmt == NULL) {
179 		fmt = fmt2;
180 	}
181 
182 	va_start(vl, ce);
183 
184 	s = va_arg(vl, char *);
185 
186 	if (ql_enable_ellock) {
187 		/*
188 		 * Used when messages are *maybe* being lost.  Adds
189 		 * a unique number to the message so one can see if
190 		 * any messages have been dropped. NB: This slows
191 		 * down the driver, which may make the issue disappear.
192 		 */
193 		GLOBAL_EL_LOCK();
194 		el_msg_num = ++el_message_number;
195 		GLOBAL_EL_UNLOCK();
196 
197 		rval = (int)snprintf(fmt, (size_t)EL_BUFFER_RESERVE,
198 		    QL_BANG "QEL%d %s(%d,%d): %s, %s", el_msg_num, QL_NAME,
199 		    ha->instance, ha->vp_index, fn, s);
200 		fmt1 = fmt + rval;
201 		tmp = (int)vsnprintf(fmt1,
202 		    (size_t)(uint32_t)((int)EL_BUFFER_RESERVE - rval), s, vl);
203 		rval += tmp;
204 	} else {
205 		rval = (int)snprintf(fmt, (size_t)EL_BUFFER_RESERVE,
206 		    QL_BANG "QEL %s(%d,%d): %s, ", QL_NAME, ha->instance,
207 		    ha->vp_index, fn);
208 		fmt1 = fmt + rval;
209 		tmp = (int)vsnprintf(fmt1,
210 		    (size_t)(uint32_t)((int)EL_BUFFER_RESERVE - rval), s, vl);
211 		rval += tmp;
212 	}
213 
214 	/*
215 	 * Calculate the offset where the next message will go,
216 	 * skipping the NULL.
217 	 */
218 	if (tracing) {
219 		rval += 1;
220 		ha->el_trace_desc->next = (uint16_t)rval;
221 		TRACE_BUFFER_UNLOCK(ha);
222 	}
223 
224 	if (ha->cfg_flags & CFG_ENABLE_EXTENDED_LOGGING) {
225 		cmn_err(ce, fmt);
226 	}
227 
228 	va_end(vl);
229 }
230 
231 /*
232  * ql_el_msg
233  *	Extended logging message
234  *
235  * Input:
236  *	ha:	adapter state pointer.
237  *	fn:	function name.
238  *	ce:	level
239  *	...:	Variable argument list.
240  *
241  * Context:
242  *	Kernel/Interrupt context.
243  */
244 void
245 ql_dbg_msg(const char *fn, int ce, ...)
246 {
247 	uint32_t	el_msg_num;
248 	char		*s;
249 	char		fmt[256];
250 	va_list		vl;
251 
252 	va_start(vl, ce);
253 
254 	s = va_arg(vl, char *);
255 
256 	if (ql_enable_ellock) {
257 		/*
258 		 * Used when messages are *maybe* being lost.  Adds
259 		 * a unique number to the message to one can see if
260 		 * any messages have been dropped. NB: This slows
261 		 * down the driver, which may make the issue disappear.
262 		 */
263 		GLOBAL_EL_LOCK();
264 		el_msg_num = ++el_message_number;
265 		GLOBAL_EL_UNLOCK();
266 		(void) snprintf(fmt, EL_BUFFER_RESERVE, "QLP%d: %s %s, %s",
267 		    el_msg_num, QL_NAME, fn, s);
268 	} else {
269 		(void) snprintf(fmt, EL_BUFFER_RESERVE, "QLP: %s %s, %s",
270 		    QL_NAME, fn, s);
271 	}
272 
273 	vcmn_err(ce, fmt, vl);
274 
275 	va_end(vl);
276 }
277 
278 /*
279  * ql_stacktrace
280  *	Prints out current stack
281  *
282  * Input:
283  *	ha:	adapter state pointer.
284  *
285  * Context:
286  *	Kernel/Interrupt context.
287  */
288 void
289 ql_stacktrace(ql_adapter_state_t *ha)
290 {
291 	int	depth, i;
292 	pc_t	pcstack[DEBUG_STK_DEPTH];
293 	char	*sym = NULL;
294 	ulong_t	off;
295 
296 	depth = getpcstack(&pcstack[0], DEBUG_STK_DEPTH);
297 
298 	cmn_err(CE_CONT, "%s(%d,%d): ---------- \n", QL_NAME, ha->instance,
299 	    ha->vp_index);
300 	for (i = 0; i < MIN(depth, DEBUG_STK_DEPTH); i++) {
301 		sym = kobj_getsymname((uintptr_t)pcstack[i], &off);
302 
303 		if (sym == NULL) {
304 			cmn_err(CE_CONT, "%s(%d,%d): sym is NULL\n", QL_NAME,
305 			    ha->instance, ha->vp_index);
306 		} else {
307 			cmn_err(CE_CONT, "%s(%d,%d): %s+%lx\n", QL_NAME,
308 			    ha->instance, ha->vp_index, sym ? sym : "?", off);
309 		}
310 	}
311 	cmn_err(CE_CONT, "%s(%d,%d): ---------- \n", QL_NAME, ha->instance,
312 	    ha->vp_index);
313 }
314 
315 /*
316  * ql_flash_errlog
317  *	Adds error to flash error log.
318  *	Entry Layout:
319  *		uint32_t TimeStamp;
320  *		uint16_t CodeData[4];
321  *
322  * Input:
323  *	ha:	adapter state pointer.
324  *	code:	Error code
325  *	d1-d3:	Error code data
326  *
327  * Returns:
328  *	ql local function return status code.
329  *
330  * Context:
331  *	Kernel/Interrupt context.
332  */
333 int
334 ql_flash_errlog(ql_adapter_state_t *ha, uint16_t code, uint16_t d1,
335     uint16_t d2, uint16_t d3)
336 {
337 	char		*s;
338 	uint32_t	marker[2], fdata[2], faddr;
339 	int		rval;
340 
341 	QL_PRINT_3(CE_CONT, "(%d): entered\n", ha->instance);
342 
343 	if (ha->flash_errlog_start == 0) {
344 		return (QL_NOT_SUPPORTED);
345 	}
346 
347 	EL(ha, "code=%xh, d1=%xh, d2=%xh, d3=%xh\n", code, d1, d2, d3);
348 
349 	/*
350 	 * If marker not already found, locate or write marker.
351 	 */
352 	if (!(ha->flags & FLASH_ERRLOG_MARKER)) {
353 
354 		/* Create marker. */
355 		marker[0] = CHAR_TO_LONG(ha->fw_subminor_version,
356 		    ha->fw_minor_version, ha->fw_major_version, 'S');
357 
358 		/*
359 		 * Version should be of the format: YYYYMMDD-v.vv
360 		 */
361 		if ((strlen(QL_VERSION) > 9) && (QL_VERSION[8] == '-')) {
362 			s = &QL_VERSION[9];
363 		} else {
364 			s = QL_VERSION;
365 		}
366 
367 		for (marker[1] = 0; *s != '\0'; s++) {
368 			if (*s >= '0' && *s <= '9') {
369 				marker[1] <<= 4;
370 				marker[1] |= *s - '0';
371 			} else if (*s != '.') {
372 				break;
373 			}
374 		}
375 
376 		/* Locate marker. */
377 		ha->flash_errlog_ptr = ha->flash_errlog_start;
378 		for (;;) {
379 			faddr = FLASH_DATA_ADDR | ha->flash_errlog_ptr;
380 			(void) ql_24xx_read_flash(ha, faddr++, &fdata[0]);
381 			(void) ql_24xx_read_flash(ha, faddr++, &fdata[1]);
382 			if (fdata[0] == 0xffffffff && fdata[1] == 0xffffffff) {
383 				break;
384 			}
385 			(void) ql_24xx_read_flash(ha, faddr++, &fdata[0]);
386 			(void) ql_24xx_read_flash(ha, faddr++, &fdata[1]);
387 			ha->flash_errlog_ptr += FLASH_ERRLOG_ENTRY_SIZE;
388 			if (ha->flash_errlog_ptr >=
389 			    ha->flash_errlog_start + FLASH_ERRLOG_SIZE) {
390 				EL(ha, "log full\n");
391 				return (QL_MEMORY_FULL);
392 			}
393 			if (fdata[0] == marker[0] && fdata[1] == marker[1]) {
394 				ha->flags |= FLASH_ERRLOG_MARKER;
395 				break;
396 			}
397 		}
398 
399 		/* No marker, write it. */
400 		if (!(ha->flags & FLASH_ERRLOG_MARKER)) {
401 			ha->flags |= FLASH_ERRLOG_MARKER;
402 			rval = ql_flash_errlog_store(ha, marker);
403 			if (rval != QL_SUCCESS) {
404 				EL(ha, "failed marker write=%xh\n", rval);
405 				return (rval);
406 			}
407 		}
408 	}
409 
410 	/*
411 	 * Store error.
412 	 */
413 	fdata[0] = SHORT_TO_LONG(d1, code);
414 	fdata[1] = SHORT_TO_LONG(d3, d2);
415 	rval = ql_flash_errlog_store(ha, fdata);
416 	if (rval != QL_SUCCESS) {
417 		EL(ha, "failed error write=%xh\n", rval);
418 	} else {
419 		/*EMPTY*/
420 		QL_PRINT_3(CE_CONT, "(%d): exiting\n", ha->instance);
421 	}
422 
423 	return (rval);
424 }
425 
426 /*
427  * ql_flash_errlog_store
428  *	Stores error to flash.
429  *	Entry Layout:
430  *		uint32_t TimeStamp;
431  *		uint16_t CodeData[4];
432  *
433  * Input:
434  *	ha:			adapter state pointer.
435  *	fdata:			Error code plus data.
436  *	ha->flash_errlog_ptr:	Current Flash error pointer.
437  *
438  * Output:
439  *	ha->flash_errlog_ptr:	updated pointer.
440  *
441  * Returns:
442  *	ql local function return status code.
443  *
444  * Context:
445  *	Kernel/Interrupt context.
446  */
447 static int
448 ql_flash_errlog_store(ql_adapter_state_t *ha, uint32_t *fdata)
449 {
450 	uint64_t	time;
451 	uint32_t	d1, d2, faddr;
452 
453 	QL_PRINT_3(CE_CONT, "(%d): entered\n", ha->instance);
454 
455 	/* Locate first empty entry */
456 	for (;;) {
457 		if (ha->flash_errlog_ptr >=
458 		    ha->flash_errlog_start + FLASH_ERRLOG_SIZE) {
459 			EL(ha, "log full\n");
460 			return (QL_MEMORY_FULL);
461 		}
462 
463 		faddr = FLASH_DATA_ADDR | ha->flash_errlog_ptr;
464 		ha->flash_errlog_ptr += FLASH_ERRLOG_ENTRY_SIZE;
465 		(void) ql_24xx_read_flash(ha, faddr, &d1);
466 		(void) ql_24xx_read_flash(ha, faddr + 1, &d2);
467 		if (d1 == 0xffffffff && d2 == 0xffffffff) {
468 			(void) drv_getparm(TIME, &time);
469 
470 			/* Enable flash write. */
471 			ql_24xx_unprotect_flash(ha);
472 
473 			(void) ql_24xx_write_flash(ha, faddr++, LSD(time));
474 			(void) ql_24xx_write_flash(ha, faddr++, MSD(time));
475 			(void) ql_24xx_write_flash(ha, faddr++, *fdata++);
476 			(void) ql_24xx_write_flash(ha, faddr++, *fdata);
477 
478 			/* Enable flash write-protection. */
479 			ql_24xx_protect_flash(ha);
480 			break;
481 		}
482 	}
483 
484 	QL_PRINT_3(CE_CONT, "(%d): exiting\n", ha->instance);
485 
486 	return (QL_SUCCESS);
487 }
488 
489 /*
490  * ql_dump_el_trace_buffer
491  *	 Outputs extended logging trace buffer.
492  *
493  * Input:
494  *	ha:	adapter state pointer.
495  */
496 void
497 ql_dump_el_trace_buffer(ql_adapter_state_t *ha)
498 {
499 	char		*dump_start = NULL;
500 	char		*dump_current = NULL;
501 	char		*trace_start;
502 	char		*trace_end;
503 	int		wrapped = 0;
504 	int		rval;
505 
506 	TRACE_BUFFER_LOCK(ha);
507 
508 	rval = ql_validate_trace_desc(ha);
509 	if (rval != NULL) {
510 		cmn_err(CE_CONT, "%s(%d) Dump EL trace - invalid desc\n",
511 		    QL_NAME, ha->instance);
512 	} else if ((dump_start = ql_find_trace_start(ha)) != NULL) {
513 		dump_current = dump_start;
514 		trace_start = ha->el_trace_desc->trace_buffer;
515 		trace_end = trace_start +
516 		    ha->el_trace_desc->trace_buffer_size;
517 
518 		cmn_err(CE_CONT, "%s(%d) Dump EL trace - start %p %p\n",
519 		    QL_NAME, ha->instance,
520 		    (void *)dump_start, (void *)trace_start);
521 
522 		while (((uintptr_t)dump_current - (uintptr_t)trace_start) <=
523 		    ha->el_trace_desc->trace_buffer_size) {
524 			/* Show it... */
525 			cmn_err(CE_CONT, "%p - %s", (void *)dump_current,
526 			    dump_current);
527 			/* Make the next the current */
528 			dump_current += (strlen(dump_current) + 1);
529 			/* check for wrap */
530 			if ((dump_current + EL_BUFFER_RESERVE) >= trace_end) {
531 				dump_current = trace_start;
532 				wrapped = 1;
533 			} else if (wrapped) {
534 				/* Don't go past next. */
535 				if ((trace_start + ha->el_trace_desc->next) <=
536 				    dump_current) {
537 					break;
538 				}
539 			} else if (*dump_current == NULL) {
540 				break;
541 			}
542 		}
543 	}
544 	TRACE_BUFFER_UNLOCK(ha);
545 }
546 
547 /*
548  * ql_validate_trace_desc
549  *	 Ensures the extended logging trace descriptor is good
550  *
551  * Input:
552  *	ha:	adapter state pointer.
553  *
554  * Returns:
555  *	ql local function return status code.
556  */
557 int
558 ql_validate_trace_desc(ql_adapter_state_t *ha)
559 {
560 	int	rval = DDI_SUCCESS;
561 
562 	if (ha->el_trace_desc == NULL) {
563 		rval = DDI_FAILURE;
564 	} else if (ha->el_trace_desc->trace_buffer == NULL) {
565 		rval = DDI_FAILURE;
566 	}
567 	return (rval);
568 }
569 
570 /*
571  * ql_find_trace_start
572  *	 Locate the oldest extended logging trace entry.
573  *
574  * Input:
575  *	ha:	adapter state pointer.
576  *
577  * Returns:
578  *	Pointer to a string.
579  *
580  * Context:
581  *	Kernel/Interrupt context.
582  */
583 char *
584 ql_find_trace_start(ql_adapter_state_t *ha)
585 {
586 	char	*trace_start = 0;
587 	char	*trace_next  = 0;
588 
589 	trace_next = ha->el_trace_desc->trace_buffer + ha->el_trace_desc->next;
590 
591 	/*
592 	 * if the buffer has not wrapped next will point at a null so
593 	 * start is the beginning of the buffer.  if next points at a char
594 	 * then we must traverse the buffer until a null is detected and
595 	 * that will be the beginning of the oldest whole object in the buffer
596 	 * which is the start.
597 	 */
598 
599 	if ((trace_next + EL_BUFFER_RESERVE) >=
600 	    (ha->el_trace_desc->trace_buffer +
601 	    ha->el_trace_desc->trace_buffer_size)) {
602 		trace_start = ha->el_trace_desc->trace_buffer;
603 	} else if (*trace_next != NULL) {
604 		trace_start = trace_next + (strlen(trace_next) + 1);
605 	} else {
606 		trace_start = ha->el_trace_desc->trace_buffer;
607 	}
608 	return (trace_start);
609 }
610