866 lines · 20.9 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/module.h>#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; 	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;		} 		/*		 * 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_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, buf_size, 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) {			if (cp < lastchar) {				memcpy(tmpbuffer, cp, lastchar - cp);				memcpy(cp-1, tmpbuffer, lastchar - cp);			}			*(--lastchar) = '\0';			--cp;			kdb_printf("\b%s \r", cp);			tmp = *cp;			*cp = '\0';			kdb_printf(kdb_prompt_str);			kdb_printf("%s", buffer);			*cp = tmp;		}		break;	case 13: /* enter */		*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) {			memcpy(tmpbuffer, cp+1, lastchar - cp - 1);			memcpy(cp, tmpbuffer, lastchar - cp - 1);			*(--lastchar) = '\0';			kdb_printf("%s \r", cp);			tmp = *cp;			*cp = '\0';			kdb_printf(kdb_prompt_str);			kdb_printf("%s", buffer);			*cp = tmp;		}		break;	case 1: /* Home */		if (cp > buffer) {			kdb_printf("\r");			kdb_printf(kdb_prompt_str);			cp = buffer;		}		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 */		memset(tmpbuffer, ' ',		       strlen(kdb_prompt_str) + (lastchar-buffer));		*(tmpbuffer+strlen(kdb_prompt_str) +		  (lastchar-buffer)) = '\0';		kdb_printf("\r%s\r", tmpbuffer);		*lastchar = (char)key;		*(lastchar+1) = '\0';		return lastchar;	case 6: /* Right */		if (cp < lastchar) {			kdb_printf("%c", *cp);			++cp;		}		break;	case 16: /* Up */		memset(tmpbuffer, ' ',		       strlen(kdb_prompt_str) + (lastchar-buffer));		*(tmpbuffer+strlen(kdb_prompt_str) +		  (lastchar-buffer)) = '\0';		kdb_printf("\r%s\r", tmpbuffer);		*lastchar = (char)key;		*(lastchar+1) = '\0';		return lastchar;	case 9: /* Tab */		if (tab < 2)			++tab;		p_tmp = buffer;		while (*p_tmp == ' ')			p_tmp++;		if (p_tmp > cp)			break;		memcpy(tmpbuffer, p_tmp, cp-p_tmp);		*(tmpbuffer + (cp-p_tmp)) = '\0';		p_tmp = strrchr(tmpbuffer, ' ');		if (p_tmp)			++p_tmp;		else			p_tmp = tmpbuffer;		len = strlen(p_tmp);		buf_size = sizeof(tmpbuffer) - (p_tmp - tmpbuffer);		count = kallsyms_symbol_complete(p_tmp, buf_size);		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(p_tmp, i, buf_size);				if (WARN_ON(!ret))					break;				if (ret != -E2BIG)					kdb_printf("%s ", p_tmp);				else					kdb_printf("%s... ", p_tmp);				*(p_tmp + len) = '\0';			}			if (i >= dtab_count)				kdb_printf("...");			kdb_printf("\n");			kdb_printf(kdb_prompt_str);			kdb_printf("%s", buffer);		} else if (tab != 2 && count > 0) {			len_tmp = strlen(p_tmp);			strncpy(p_tmp+len_tmp, cp, lastchar-cp+1);			len_tmp = strlen(p_tmp);			strncpy(cp, p_tmp+len, len_tmp-len + 1);			len = len_tmp - len;			kdb_printf("%s", cp);			cp += len;			lastchar += len;		}		kdb_nextline = 1; /* reset output line number */		break;	default:		if (key >= 32 && lastchar < bufend) {			if (cp < lastchar) {				memcpy(tmpbuffer, cp, lastchar - cp);				memcpy(cp+1, tmpbuffer, lastchar - cp);				*++lastchar = '\0';				*cp = key;				kdb_printf("%s\r", cp);				++cp;				tmp = *cp;				*cp = '\0';				kdb_printf(kdb_prompt_str);				kdb_printf("%s", buffer);				*cp = tmp;			} 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(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 * *	Print a string to the output device(s). * * Parameters: *	printf-like format and optional args. * Returns: *	0 * Locking: *	None. * Remarks: *	use 'kdbcons->write()' to avoid polluting 'log_buf' with *	kdb output. * *  If the user is doing a cmd args | grep srch *  then kdb_grepping_flag is set. *  In that case we need to accumulate full lines (ending in \n) before

Lines 1–500 of 866.