902 lines · 22.6 KB · C
RawDownload
/* * Kernel Debugger Architecture Independent Console I/O handler * * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (c) 1999-2006 Silicon Graphics, Inc.  All Rights Reserved. * Copyright (c) 2009 Wind River Systems, Inc.  All Rights Reserved. */ #include <linux/types.h>#include <linux/ctype.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/kdev_t.h>#include <linux/console.h>#include <linux/string.h>#include <linux/sched.h>#include <linux/smp.h>#include <linux/nmi.h>#include <linux/delay.h>#include <linux/kgdb.h>#include <linux/kdb.h>#include <linux/kallsyms.h>#include "kdb_private.h" #define CMD_BUFLEN 256char kdb_prompt_str[CMD_BUFLEN]; int kdb_trap_printk;int kdb_printf_cpu = -1; static int kgdb_transition_check(char *buffer){	if (buffer[0] != '+' && buffer[0] != '$') {		KDB_STATE_SET(KGDB_TRANS);		kdb_printf("%s", buffer);	} else {		int slen = strlen(buffer);		if (slen > 3 && buffer[slen - 3] == '#') {			kdb_gdb_state_pass(buffer);			strcpy(buffer, "kgdb");			KDB_STATE_SET(DOING_KGDB);			return 1;		}	}	return 0;} /** * kdb_handle_escape() - validity check on an accumulated escape sequence. * @buf:	Accumulated escape characters to be examined. Note that buf *		is not a string, it is an array of characters and need not be *		nil terminated. * @sz:		Number of accumulated escape characters. * * Return: -1 if the escape sequence is unwanted, 0 if it is incomplete, * otherwise it returns a mapped key value to pass to the upper layers. */static int kdb_handle_escape(char *buf, size_t sz){	char *lastkey = buf + sz - 1; 	switch (sz) {	case 1:		if (*lastkey == '\e')			return 0;		break; 	case 2: /* \e<something> */		if (*lastkey == '[')			return 0;		break; 	case 3:		switch (*lastkey) {		case 'A': /* \e[A, up arrow */			return 16;		case 'B': /* \e[B, down arrow */			return 14;		case 'C': /* \e[C, right arrow */			return 6;		case 'D': /* \e[D, left arrow */			return 2;		case '1': /* \e[<1,3,4>], may be home, del, end */		case '3':		case '4':			return 0;		}		break; 	case 4:		if (*lastkey == '~') {			switch (buf[2]) {			case '1': /* \e[1~, home */				return 1;			case '3': /* \e[3~, del */				return 4;			case '4': /* \e[4~, end */				return 5;			}		}		break;	} 	return -1;} /** * kdb_getchar() - Read a single character from a kdb console (or consoles). * * Other than polling the various consoles that are currently enabled, * most of the work done in this function is dealing with escape sequences. * * An escape key could be the start of a vt100 control sequence such as \e[D * (left arrow) or it could be a character in its own right.  The standard * method for detecting the difference is to wait for 2 seconds to see if there * are any other characters.  kdb is complicated by the lack of a timer service * (interrupts are off), by multiple input sources. Escape sequence processing * has to be done as states in the polling loop. * * Return: The key pressed or a control code derived from an escape sequence. */char kdb_getchar(void){#define ESCAPE_UDELAY 1000#define ESCAPE_DELAY (2*1000000/ESCAPE_UDELAY) /* 2 seconds worth of udelays */	char buf[4];	/* longest vt100 escape sequence is 4 bytes */	char *pbuf = buf;	int escape_delay = 0;	get_char_func *f, *f_prev = NULL;	int key;	static bool last_char_was_cr; 	for (f = &kdb_poll_funcs[0]; ; ++f) {		if (*f == NULL) {			/* Reset NMI watchdog once per poll loop */			touch_nmi_watchdog();			f = &kdb_poll_funcs[0];		} 		key = (*f)();		if (key == -1) {			if (escape_delay) {				udelay(ESCAPE_UDELAY);				if (--escape_delay == 0)					return '\e';			}			continue;		} 		/*		 * The caller expects that newlines are either CR or LF. However		 * some terminals send _both_ CR and LF. Avoid having to handle		 * this in the caller by stripping the LF if we saw a CR right		 * before.		 */		if (last_char_was_cr && key == '\n') {			last_char_was_cr = false;			continue;		}		last_char_was_cr = (key == '\r'); 		/*		 * When the first character is received (or we get a change		 * input source) we set ourselves up to handle an escape		 * sequences (just in case).		 */		if (f_prev != f) {			f_prev = f;			pbuf = buf;			escape_delay = ESCAPE_DELAY;		} 		*pbuf++ = key;		key = kdb_handle_escape(buf, pbuf - buf);		if (key < 0) /* no escape sequence; return best character */			return buf[pbuf - buf == 2 ? 1 : 0];		if (key > 0)			return key;	} 	unreachable();} /** * kdb_position_cursor() - Place cursor in the correct horizontal position * @prompt: Nil-terminated string containing the prompt string * @buffer: Nil-terminated string containing the entire command line * @cp: Cursor position, pointer the character in buffer where the cursor *      should be positioned. * * The cursor is positioned by sending a carriage-return and then printing * the content of the line until we reach the correct cursor position. * * There is some additional fine detail here. * * Firstly, even though kdb_printf() will correctly format zero-width fields * we want the second call to kdb_printf() to be conditional. That keeps things * a little cleaner when LOGGING=1. * * Secondly, we can't combine everything into one call to kdb_printf() since * that renders into a fixed length buffer and the combined print could result * in unwanted truncation. */static void kdb_position_cursor(char *prompt, char *buffer, char *cp){	kdb_printf("\r%s", prompt);	if (cp > buffer)		kdb_printf("%.*s", (int)(cp - buffer), buffer);} /* * kdb_read * *	This function reads a string of characters, terminated by *	a newline, or by reaching the end of the supplied buffer, *	from the current kernel debugger console device. * Parameters: *	buffer	- Address of character buffer to receive input characters. *	bufsize - size, in bytes, of the character buffer * Returns: *	Returns a pointer to the buffer containing the received *	character string.  This string will be terminated by a *	newline character. * Locking: *	No locks are required to be held upon entry to this *	function.  It is not reentrant - it relies on the fact *	that while kdb is running on only one "master debug" cpu. * Remarks: *	The buffer size must be >= 2. */ static char *kdb_read(char *buffer, size_t bufsize){	char *cp = buffer;	char *bufend = buffer+bufsize-2;	/* Reserve space for newline						 * and null byte */	char *lastchar;	char *p_tmp;	char tmp;	static char tmpbuffer[CMD_BUFLEN];	int len = strlen(buffer);	int len_tmp;	int tab = 0;	int count;	int i;	int diag, dtab_count;	int key, ret; 	diag = kdbgetintenv("DTABCOUNT", &dtab_count);	if (diag)		dtab_count = 30; 	if (len > 0) {		cp += len;		if (*(buffer+len-1) == '\n')			cp--;	} 	lastchar = cp;	*cp = '\0';	kdb_printf("%s", buffer);poll_again:	key = kdb_getchar();	if (key != 9)		tab = 0;	switch (key) {	case 8: /* backspace */		if (cp > buffer) {			memmove(cp-1, cp, lastchar - cp + 1);			lastchar--;			cp--;			kdb_printf("\b%s ", cp);			kdb_position_cursor(kdb_prompt_str, buffer, cp);		}		break;	case 10: /* linefeed */	case 13: /* carriage return */		*lastchar++ = '\n';		*lastchar++ = '\0';		if (!KDB_STATE(KGDB_TRANS)) {			KDB_STATE_SET(KGDB_TRANS);			kdb_printf("%s", buffer);		}		kdb_printf("\n");		return buffer;	case 4: /* Del */		if (cp < lastchar) {			memmove(cp, cp+1, lastchar - cp);			lastchar--;			kdb_printf("%s ", cp);			kdb_position_cursor(kdb_prompt_str, buffer, cp);		}		break;	case 1: /* Home */		if (cp > buffer) {			cp = buffer;			kdb_position_cursor(kdb_prompt_str, buffer, cp);		}		break;	case 5: /* End */		if (cp < lastchar) {			kdb_printf("%s", cp);			cp = lastchar;		}		break;	case 2: /* Left */		if (cp > buffer) {			kdb_printf("\b");			--cp;		}		break;	case 14: /* Down */	case 16: /* Up */		kdb_printf("\r%*c\r",			   (int)(strlen(kdb_prompt_str) + (lastchar - buffer)),			   ' ');		*lastchar = (char)key;		*(lastchar+1) = '\0';		return lastchar;	case 6: /* Right */		if (cp < lastchar) {			kdb_printf("%c", *cp);			++cp;		}		break;	case 9: /* Tab */		if (tab < 2)			++tab; 		tmp = *cp;		*cp = '\0';		p_tmp = strrchr(buffer, ' ');		p_tmp = (p_tmp ? p_tmp + 1 : buffer);		strscpy(tmpbuffer, p_tmp, sizeof(tmpbuffer));		*cp = tmp; 		len = strlen(tmpbuffer);		count = kallsyms_symbol_complete(tmpbuffer, sizeof(tmpbuffer));		if (tab == 2 && count > 0) {			kdb_printf("\n%d symbols are found.", count);			if (count > dtab_count) {				count = dtab_count;				kdb_printf(" But only first %d symbols will"					   " be printed.\nYou can change the"					   " environment variable DTABCOUNT.",					   count);			}			kdb_printf("\n");			for (i = 0; i < count; i++) {				ret = kallsyms_symbol_next(tmpbuffer, i, sizeof(tmpbuffer));				if (WARN_ON(!ret))					break;				if (ret != -E2BIG)					kdb_printf("%s ", tmpbuffer);				else					kdb_printf("%s... ", tmpbuffer);				tmpbuffer[len] = '\0';			}			if (i >= dtab_count)				kdb_printf("...");			kdb_printf("\n");			kdb_printf("%s",  kdb_prompt_str);			kdb_printf("%s", buffer);			if (cp != lastchar)				kdb_position_cursor(kdb_prompt_str, buffer, cp);		} else if (tab != 2 && count > 0) {			/* How many new characters do we want from tmpbuffer? */			len_tmp = strlen(tmpbuffer) - len;			if (lastchar + len_tmp >= bufend)				len_tmp = bufend - lastchar; 			if (len_tmp) {				/* + 1 ensures the '\0' is memmove'd */				memmove(cp+len_tmp, cp, (lastchar-cp) + 1);				memcpy(cp, tmpbuffer+len, len_tmp);				kdb_printf("%s", cp);				cp += len_tmp;				lastchar += len_tmp;				if (cp != lastchar)					kdb_position_cursor(kdb_prompt_str,							    buffer, cp);			}		}		kdb_nextline = 1; /* reset output line number */		break;	default:		if (key >= 32 && lastchar < bufend) {			if (cp < lastchar) {				memmove(cp+1, cp, lastchar - cp + 1);				lastchar++;				*cp = key;				kdb_printf("%s", cp);				++cp;				kdb_position_cursor(kdb_prompt_str, buffer, cp);			} else {				*++lastchar = '\0';				*cp++ = key;				/* The kgdb transition check will hide				 * printed characters if we think that				 * kgdb is connecting, until the check				 * fails */				if (!KDB_STATE(KGDB_TRANS)) {					if (kgdb_transition_check(buffer))						return buffer;				} else {					kdb_printf("%c", key);				}			}			/* Special escape to kgdb */			if (lastchar - buffer >= 5 &&			    strcmp(lastchar - 5, "$?#3f") == 0) {				kdb_gdb_state_pass(lastchar - 5);				strcpy(buffer, "kgdb");				KDB_STATE_SET(DOING_KGDB);				return buffer;			}			if (lastchar - buffer >= 11 &&			    strcmp(lastchar - 11, "$qSupported") == 0) {				kdb_gdb_state_pass(lastchar - 11);				strcpy(buffer, "kgdb");				KDB_STATE_SET(DOING_KGDB);				return buffer;			}		}		break;	}	goto poll_again;} /* * kdb_getstr * *	Print the prompt string and read a command from the *	input device. * * Parameters: *	buffer	Address of buffer to receive command *	bufsize Size of buffer in bytes *	prompt	Pointer to string to use as prompt string * Returns: *	Pointer to command buffer. * Locking: *	None. * Remarks: *	For SMP kernels, the processor number will be *	substituted for %d, %x or %o in the prompt. */ char *kdb_getstr(char *buffer, size_t bufsize, const char *prompt){	if (prompt && kdb_prompt_str != prompt)		strscpy(kdb_prompt_str, prompt, CMD_BUFLEN);	kdb_printf("%s", kdb_prompt_str);	kdb_nextline = 1;	/* Prompt and input resets line number */	return kdb_read(buffer, bufsize);} /* * kdb_input_flush * *	Get rid of any buffered console input. * * Parameters: *	none * Returns: *	nothing * Locking: *	none * Remarks: *	Call this function whenever you want to flush input.  If there is any *	outstanding input, it ignores all characters until there has been no *	data for approximately 1ms. */ static void kdb_input_flush(void){	get_char_func *f;	int res;	int flush_delay = 1;	while (flush_delay) {		flush_delay--;empty:		touch_nmi_watchdog();		for (f = &kdb_poll_funcs[0]; *f; ++f) {			res = (*f)();			if (res != -1) {				flush_delay = 1;				goto empty;			}		}		if (flush_delay)			mdelay(1);	}} /* * kdb_printf

Lines 1–500 of 902.