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 ((CFG_IST(ha, 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, ", el_msg_num, QL_NAME,
199 		    ha->instance, ha->vp_index, fn);
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 		uint16_t next = (uint16_t)(rval += 1);
220 		ha->el_trace_desc->next += next;
221 		TRACE_BUFFER_UNLOCK(ha);
222 	}
223 
224 	if (CFG_IST(ha, 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): started\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 = ha->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): done\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 	int		rval;
451 	uint64_t	time;
452 	uint32_t	d1, d2, faddr;
453 
454 	QL_PRINT_3(CE_CONT, "(%d): started\n", ha->instance);
455 
456 	/* Locate first empty entry */
457 	for (;;) {
458 		if (ha->flash_errlog_ptr >=
459 		    ha->flash_errlog_start + FLASH_ERRLOG_SIZE) {
460 			EL(ha, "log full\n");
461 			return (QL_MEMORY_FULL);
462 		}
463 
464 		faddr = ha->flash_data_addr | ha->flash_errlog_ptr;
465 		ha->flash_errlog_ptr += FLASH_ERRLOG_ENTRY_SIZE;
466 		(void) ql_24xx_read_flash(ha, faddr, &d1);
467 		(void) ql_24xx_read_flash(ha, faddr + 1, &d2);
468 		if (d1 == 0xffffffff && d2 == 0xffffffff) {
469 			(void) drv_getparm(TIME, &time);
470 
471 			/* Enable flash write. */
472 			if ((rval = ql_24xx_unprotect_flash(ha)) !=
473 			    QL_SUCCESS) {
474 				EL(ha, "unprotect_flash failed, rval=%xh\n",
475 				    rval);
476 				return (rval);
477 			}
478 
479 			(void) ql_24xx_write_flash(ha, faddr++, LSD(time));
480 			(void) ql_24xx_write_flash(ha, faddr++, MSD(time));
481 			(void) ql_24xx_write_flash(ha, faddr++, *fdata++);
482 			(void) ql_24xx_write_flash(ha, faddr++, *fdata);
483 
484 			/* Enable flash write-protection. */
485 			ql_24xx_protect_flash(ha);
486 			break;
487 		}
488 	}
489 
490 	QL_PRINT_3(CE_CONT, "(%d): done\n", ha->instance);
491 
492 	return (QL_SUCCESS);
493 }
494 
495 /*
496  * ql_dump_el_trace_buffer
497  *	 Outputs extended logging trace buffer.
498  *
499  * Input:
500  *	ha:	adapter state pointer.
501  */
502 void
503 ql_dump_el_trace_buffer(ql_adapter_state_t *ha)
504 {
505 	char		*dump_start = NULL;
506 	char		*dump_current = NULL;
507 	char		*trace_start;
508 	char		*trace_end;
509 	int		wrapped = 0;
510 	int		rval;
511 
512 	TRACE_BUFFER_LOCK(ha);
513 
514 	rval = ql_validate_trace_desc(ha);
515 	if (rval != 0) {
516 		cmn_err(CE_CONT, "%s(%d) Dump EL trace - invalid desc\n",
517 		    QL_NAME, ha->instance);
518 	} else if ((dump_start = ql_find_trace_start(ha)) != NULL) {
519 		dump_current = dump_start;
520 		trace_start = ha->el_trace_desc->trace_buffer;
521 		trace_end = trace_start +
522 		    ha->el_trace_desc->trace_buffer_size;
523 
524 		cmn_err(CE_CONT, "%s(%d) Dump EL trace - start %p %p\n",
525 		    QL_NAME, ha->instance,
526 		    (void *)dump_start, (void *)trace_start);
527 
528 		while (((uintptr_t)dump_current - (uintptr_t)trace_start) <=
529 		    (uintptr_t)ha->el_trace_desc->trace_buffer_size) {
530 			/* Show it... */
531 			cmn_err(CE_CONT, "%p - %s", (void *)dump_current,
532 			    dump_current);
533 			/* Make the next the current */
534 			dump_current += (strlen(dump_current) + 1);
535 			/* check for wrap */
536 			if ((dump_current + EL_BUFFER_RESERVE) >= trace_end) {
537 				dump_current = trace_start;
538 				wrapped = 1;
539 			} else if (wrapped) {
540 				/* Don't go past next. */
541 				if ((trace_start + ha->el_trace_desc->next) <=
542 				    dump_current) {
543 					break;
544 				}
545 			} else if (*dump_current == '\0') {
546 				break;
547 			}
548 		}
549 	}
550 	TRACE_BUFFER_UNLOCK(ha);
551 }
552 
553 /*
554  * ql_validate_trace_desc
555  *	 Ensures the extended logging trace descriptor is good
556  *
557  * Input:
558  *	ha:	adapter state pointer.
559  *
560  * Returns:
561  *	ql local function return status code.
562  */
563 int
564 ql_validate_trace_desc(ql_adapter_state_t *ha)
565 {
566 	int	rval = DDI_SUCCESS;
567 
568 	if (ha->el_trace_desc == NULL) {
569 		rval = DDI_FAILURE;
570 	} else if (ha->el_trace_desc->trace_buffer == NULL) {
571 		rval = DDI_FAILURE;
572 	}
573 	return (rval);
574 }
575 
576 /*
577  * ql_find_trace_start
578  *	 Locate the oldest extended logging trace entry.
579  *
580  * Input:
581  *	ha:	adapter state pointer.
582  *
583  * Returns:
584  *	Pointer to a string.
585  *
586  * Context:
587  *	Kernel/Interrupt context.
588  */
589 char *
590 ql_find_trace_start(ql_adapter_state_t *ha)
591 {
592 	char	*trace_start = 0;
593 	char	*trace_next  = 0;
594 
595 	trace_next = ha->el_trace_desc->trace_buffer + ha->el_trace_desc->next;
596 
597 	/*
598 	 * if the buffer has not wrapped next will point at a null so
599 	 * start is the beginning of the buffer.  if next points at a char
600 	 * then we must traverse the buffer until a null is detected and
601 	 * that will be the beginning of the oldest whole object in the buffer
602 	 * which is the start.
603 	 */
604 
605 	if ((trace_next + EL_BUFFER_RESERVE) >=
606 	    (ha->el_trace_desc->trace_buffer +
607 	    ha->el_trace_desc->trace_buffer_size)) {
608 		trace_start = ha->el_trace_desc->trace_buffer;
609 	} else if (*trace_next != '\0') {
610 		trace_start = trace_next + (strlen(trace_next) + 1);
611 	} else {
612 		trace_start = ha->el_trace_desc->trace_buffer;
613 	}
614 	return (trace_start);
615 }
616