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