Commit 4f27e824 authored by Daniel Thompson's avatar Daniel Thompson

kdb: Remove special case logic from kdb_read()

kdb_read() contains special case logic to force it exit after reading
a single character. We can remove all the special case logic by directly
calling the function to read a single character instead. This also
allows us to tidy up the function prototype which, because it now matches
getchar(), we can also rename in order to make its role clearer.

This does involve some extra code to handle btaprompt properly but we
don't mind the new lines of code here because the old code had some
interesting problems (bad newline handling, treating unexpected
characters like <cr>).
Signed-off-by: default avatarDaniel Thompson <daniel.thompson@linaro.org>
Reviewed-by: default avatarDouglas Anderson <dianders@chromium.org>
Link: https://lore.kernel.org/r/20191025073328.643-4-daniel.thompson@linaro.org
parent d04213af
...@@ -75,9 +75,10 @@ static void kdb_show_stack(struct task_struct *p, void *addr) ...@@ -75,9 +75,10 @@ static void kdb_show_stack(struct task_struct *p, void *addr)
static int static int
kdb_bt1(struct task_struct *p, unsigned long mask, bool btaprompt) kdb_bt1(struct task_struct *p, unsigned long mask, bool btaprompt)
{ {
char buffer[2]; char ch;
if (kdb_getarea(buffer[0], (unsigned long)p) ||
kdb_getarea(buffer[0], (unsigned long)(p+1)-1)) if (kdb_getarea(ch, (unsigned long)p) ||
kdb_getarea(ch, (unsigned long)(p+1)-1))
return KDB_BADADDR; return KDB_BADADDR;
if (!kdb_task_state(p, mask)) if (!kdb_task_state(p, mask))
return 0; return 0;
...@@ -85,13 +86,18 @@ kdb_bt1(struct task_struct *p, unsigned long mask, bool btaprompt) ...@@ -85,13 +86,18 @@ kdb_bt1(struct task_struct *p, unsigned long mask, bool btaprompt)
kdb_ps1(p); kdb_ps1(p);
kdb_show_stack(p, NULL); kdb_show_stack(p, NULL);
if (btaprompt) { if (btaprompt) {
kdb_getstr(buffer, sizeof(buffer), kdb_printf("Enter <q> to end, <cr> or <space> to continue:");
"Enter <q> to end, <cr> to continue:"); do {
if (buffer[0] == 'q') { ch = kdb_getchar();
} while (!strchr("\r\n q", ch));
kdb_printf("\n"); kdb_printf("\n");
/* reset the pager */
kdb_nextline = 1;
if (ch == 'q')
return 1; return 1;
} }
}
touch_nmi_watchdog(); touch_nmi_watchdog();
return 0; return 0;
} }
......
...@@ -108,7 +108,22 @@ static int kdb_handle_escape(char *buf, size_t sz) ...@@ -108,7 +108,22 @@ static int kdb_handle_escape(char *buf, size_t sz)
return -1; return -1;
} }
static int kdb_read_get_key(char *buffer, size_t bufsize) /**
* 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_UDELAY 1000
#define ESCAPE_DELAY (2*1000000/ESCAPE_UDELAY) /* 2 seconds worth of udelays */ #define ESCAPE_DELAY (2*1000000/ESCAPE_UDELAY) /* 2 seconds worth of udelays */
...@@ -126,7 +141,6 @@ static int kdb_read_get_key(char *buffer, size_t bufsize) ...@@ -126,7 +141,6 @@ static int kdb_read_get_key(char *buffer, size_t bufsize)
} }
key = (*f)(); key = (*f)();
if (key == -1) { if (key == -1) {
if (escape_delay) { if (escape_delay) {
udelay(ESCAPE_UDELAY); udelay(ESCAPE_UDELAY);
...@@ -136,14 +150,6 @@ static int kdb_read_get_key(char *buffer, size_t bufsize) ...@@ -136,14 +150,6 @@ static int kdb_read_get_key(char *buffer, size_t bufsize)
continue; continue;
} }
if (bufsize <= 2) {
if (key == '\r')
key = '\n';
*buffer++ = key;
*buffer = '\0';
return -1;
}
if (escape_delay == 0 && key == '\e') { if (escape_delay == 0 && key == '\e') {
escape_delay = ESCAPE_DELAY; escape_delay = ESCAPE_DELAY;
ped = escape_data; ped = escape_data;
...@@ -184,17 +190,7 @@ static int kdb_read_get_key(char *buffer, size_t bufsize) ...@@ -184,17 +190,7 @@ static int kdb_read_get_key(char *buffer, size_t bufsize)
* function. It is not reentrant - it relies on the fact * function. It is not reentrant - it relies on the fact
* that while kdb is running on only one "master debug" cpu. * that while kdb is running on only one "master debug" cpu.
* Remarks: * Remarks:
* * The buffer size must be >= 2.
* The buffer size must be >= 2. A buffer size of 2 means that the caller only
* wants a single key.
*
* 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 and by the need to sometimes
* return after just one key. Escape sequence processing has to be done as
* states in the polling loop.
*/ */
static char *kdb_read(char *buffer, size_t bufsize) static char *kdb_read(char *buffer, size_t bufsize)
...@@ -229,9 +225,7 @@ static char *kdb_read(char *buffer, size_t bufsize) ...@@ -229,9 +225,7 @@ static char *kdb_read(char *buffer, size_t bufsize)
*cp = '\0'; *cp = '\0';
kdb_printf("%s", buffer); kdb_printf("%s", buffer);
poll_again: poll_again:
key = kdb_read_get_key(buffer, bufsize); key = kdb_getchar();
if (key == -1)
return buffer;
if (key != 9) if (key != 9)
tab = 0; tab = 0;
switch (key) { switch (key) {
...@@ -742,7 +736,7 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap) ...@@ -742,7 +736,7 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
/* check for having reached the LINES number of printed lines */ /* check for having reached the LINES number of printed lines */
if (kdb_nextline >= linecount) { if (kdb_nextline >= linecount) {
char buf1[16] = ""; char ch;
/* Watch out for recursion here. Any routine that calls /* Watch out for recursion here. Any routine that calls
* kdb_printf will come back through here. And kdb_read * kdb_printf will come back through here. And kdb_read
...@@ -777,39 +771,38 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap) ...@@ -777,39 +771,38 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
if (logging) if (logging)
printk("%s", moreprompt); printk("%s", moreprompt);
kdb_read(buf1, 2); /* '2' indicates to return ch = kdb_getchar();
* immediately after getting one key. */
kdb_nextline = 1; /* Really set output line 1 */ kdb_nextline = 1; /* Really set output line 1 */
/* empty and reset the buffer: */ /* empty and reset the buffer: */
kdb_buffer[0] = '\0'; kdb_buffer[0] = '\0';
next_avail = kdb_buffer; next_avail = kdb_buffer;
size_avail = sizeof(kdb_buffer); size_avail = sizeof(kdb_buffer);
if ((buf1[0] == 'q') || (buf1[0] == 'Q')) { if ((ch == 'q') || (ch == 'Q')) {
/* user hit q or Q */ /* user hit q or Q */
KDB_FLAG_SET(CMD_INTERRUPT); /* command interrupted */ KDB_FLAG_SET(CMD_INTERRUPT); /* command interrupted */
KDB_STATE_CLEAR(PAGER); KDB_STATE_CLEAR(PAGER);
/* end of command output; back to normal mode */ /* end of command output; back to normal mode */
kdb_grepping_flag = 0; kdb_grepping_flag = 0;
kdb_printf("\n"); kdb_printf("\n");
} else if (buf1[0] == ' ') { } else if (ch == ' ') {
kdb_printf("\r"); kdb_printf("\r");
suspend_grep = 1; /* for this recursion */ suspend_grep = 1; /* for this recursion */
} else if (buf1[0] == '\n') { } else if (ch == '\n' || ch == '\r') {
kdb_nextline = linecount - 1; kdb_nextline = linecount - 1;
kdb_printf("\r"); kdb_printf("\r");
suspend_grep = 1; /* for this recursion */ suspend_grep = 1; /* for this recursion */
} else if (buf1[0] == '/' && !kdb_grepping_flag) { } else if (ch == '/' && !kdb_grepping_flag) {
kdb_printf("\r"); kdb_printf("\r");
kdb_getstr(kdb_grep_string, KDB_GREP_STRLEN, kdb_getstr(kdb_grep_string, KDB_GREP_STRLEN,
kdbgetenv("SEARCHPROMPT") ?: "search> "); kdbgetenv("SEARCHPROMPT") ?: "search> ");
*strchrnul(kdb_grep_string, '\n') = '\0'; *strchrnul(kdb_grep_string, '\n') = '\0';
kdb_grepping_flag += KDB_GREPPING_FLAG_SEARCH; kdb_grepping_flag += KDB_GREPPING_FLAG_SEARCH;
suspend_grep = 1; /* for this recursion */ suspend_grep = 1; /* for this recursion */
} else if (buf1[0] && buf1[0] != '\n') { } else if (ch) {
/* user hit something other than enter */ /* user hit something unexpected */
suspend_grep = 1; /* for this recursion */ suspend_grep = 1; /* for this recursion */
if (buf1[0] != '/') if (ch != '/')
kdb_printf( kdb_printf(
"\nOnly 'q', 'Q' or '/' are processed at " "\nOnly 'q', 'Q' or '/' are processed at "
"more prompt, input ignored\n"); "more prompt, input ignored\n");
......
...@@ -210,6 +210,7 @@ extern void kdb_ps1(const struct task_struct *p); ...@@ -210,6 +210,7 @@ extern void kdb_ps1(const struct task_struct *p);
extern void kdb_print_nameval(const char *name, unsigned long val); extern void kdb_print_nameval(const char *name, unsigned long val);
extern void kdb_send_sig(struct task_struct *p, int sig); extern void kdb_send_sig(struct task_struct *p, int sig);
extern void kdb_meminfo_proc_show(void); extern void kdb_meminfo_proc_show(void);
extern char kdb_getchar(void);
extern char *kdb_getstr(char *, size_t, const char *); extern char *kdb_getstr(char *, size_t, const char *);
extern void kdb_gdb_state_pass(char *buf); extern void kdb_gdb_state_pass(char *buf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment