Skip to content

Commit

Permalink
kgdb,8250,pl011: Return immediately from console poll
Browse files Browse the repository at this point in the history
The design of the kdb shell requires that every device that can
provide input to kdb have a polling routine that exits immediately if
there is no character available.  This is required in order to get the
page scrolling mechanism working.

Changing the kernel debugger I/O API to require all polling character
routines to exit immediately if there is no data allows the kernel
debugger to process multiple input channels.

NO_POLL_CHAR will be the return code to the polling routine when ever
there is no character available.

CC: [email protected]
Signed-off-by: Jason Wessel <[email protected]>
  • Loading branch information
jwessel committed May 21, 2010
1 parent dcc7871 commit f5316b4
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 11 deletions.
4 changes: 2 additions & 2 deletions drivers/serial/8250.c
Original file line number Diff line number Diff line change
Expand Up @@ -1891,8 +1891,8 @@ static int serial8250_get_poll_char(struct uart_port *port)
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned char lsr = serial_inp(up, UART_LSR);

while (!(lsr & UART_LSR_DR))
lsr = serial_inp(up, UART_LSR);
if (!(lsr & UART_LSR_DR))
return NO_POLL_CHAR;

return serial_inp(up, UART_RX);
}
Expand Down
6 changes: 3 additions & 3 deletions drivers/serial/amba-pl011.c
Original file line number Diff line number Diff line change
Expand Up @@ -342,9 +342,9 @@ static int pl010_get_poll_char(struct uart_port *port)
struct uart_amba_port *uap = (struct uart_amba_port *)port;
unsigned int status;

do {
status = readw(uap->port.membase + UART01x_FR);
} while (status & UART01x_FR_RXFE);
status = readw(uap->port.membase + UART01x_FR);
if (status & UART01x_FR_RXFE)
return NO_POLL_CHAR;

return readw(uap->port.membase + UART01x_DR);
}
Expand Down
1 change: 1 addition & 0 deletions include/linux/kdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <asm/atomic.h>

#define KDB_POLL_FUNC_MAX 5
extern int kdb_poll_idx;

/*
* kdb_initial_cpu is initialized to -1, and is set to the cpu
Expand Down
1 change: 1 addition & 0 deletions include/linux/serial_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ struct uart_ops {
#endif
};

#define NO_POLL_CHAR 0x00ff0000
#define UART_CONFIG_TYPE (1 << 0)
#define UART_CONFIG_IRQ (1 << 1)

Expand Down
2 changes: 2 additions & 0 deletions kernel/debug/debug_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,8 @@ EXPORT_SYMBOL_GPL(kgdb_unregister_io_module);
int dbg_io_get_char(void)
{
int ret = dbg_io_ops->read_char();
if (ret == NO_POLL_CHAR)
return -1;
if (!dbg_kdb_mode)
return ret;
if (ret == 127)
Expand Down
37 changes: 31 additions & 6 deletions kernel/debug/gdbstub.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include <linux/kernel.h>
#include <linux/kgdb.h>
#include <linux/kdb.h>
#include <linux/reboot.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
Expand Down Expand Up @@ -62,6 +63,30 @@ static int hex(char ch)
return -1;
}

#ifdef CONFIG_KGDB_KDB
static int gdbstub_read_wait(void)
{
int ret = -1;
int i;

/* poll any additional I/O interfaces that are defined */
while (ret < 0)
for (i = 0; kdb_poll_funcs[i] != NULL; i++) {
ret = kdb_poll_funcs[i]();
if (ret > 0)
break;
}
return ret;
}
#else
static int gdbstub_read_wait(void)
{
int ret = dbg_io_ops->read_char();
while (ret == NO_POLL_CHAR)
ret = dbg_io_ops->read_char();
return ret;
}
#endif
/* scan for the sequence $<data>#<checksum> */
static void get_packet(char *buffer)
{
Expand All @@ -75,7 +100,7 @@ static void get_packet(char *buffer)
* Spin and wait around for the start character, ignore all
* other characters:
*/
while ((ch = (dbg_io_ops->read_char())) != '$')
while ((ch = (gdbstub_read_wait())) != '$')
/* nothing */;

kgdb_connected = 1;
Expand All @@ -88,7 +113,7 @@ static void get_packet(char *buffer)
* now, read until a # or end of buffer is found:
*/
while (count < (BUFMAX - 1)) {
ch = dbg_io_ops->read_char();
ch = gdbstub_read_wait();
if (ch == '#')
break;
checksum = checksum + ch;
Expand All @@ -98,8 +123,8 @@ static void get_packet(char *buffer)
buffer[count] = 0;

if (ch == '#') {
xmitcsum = hex(dbg_io_ops->read_char()) << 4;
xmitcsum += hex(dbg_io_ops->read_char());
xmitcsum = hex(gdbstub_read_wait()) << 4;
xmitcsum += hex(gdbstub_read_wait());

if (checksum != xmitcsum)
/* failed checksum */
Expand Down Expand Up @@ -144,10 +169,10 @@ static void put_packet(char *buffer)
dbg_io_ops->flush();

/* Now see what we get in reply. */
ch = dbg_io_ops->read_char();
ch = gdbstub_read_wait();

if (ch == 3)
ch = dbg_io_ops->read_char();
ch = gdbstub_read_wait();

/* If we get an ACK, we are done. */
if (ch == '+')
Expand Down
10 changes: 10 additions & 0 deletions kernel/debug/kdb/kdb_debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@
get_char_func kdb_poll_funcs[] = {
dbg_io_get_char,
NULL,
NULL,
NULL,
NULL,
NULL,
};
EXPORT_SYMBOL_GPL(kdb_poll_funcs);

int kdb_poll_idx = 1;
EXPORT_SYMBOL_GPL(kdb_poll_idx);

int kdb_stub(struct kgdb_state *ks)
{
Expand Down Expand Up @@ -85,6 +93,7 @@ int kdb_stub(struct kgdb_state *ks)
kdb_bp_remove();
KDB_STATE_CLEAR(DOING_SS);
KDB_STATE_CLEAR(DOING_SSB);
KDB_STATE_SET(PAGER);
/* zero out any offline cpu data */
for_each_present_cpu(i) {
if (!cpu_online(i)) {
Expand Down Expand Up @@ -112,6 +121,7 @@ int kdb_stub(struct kgdb_state *ks)
kdb_initial_cpu = -1;
kdb_current_task = NULL;
kdb_current_regs = NULL;
KDB_STATE_CLEAR(PAGER);
kdbnearsym_cleanup();
if (error == KDB_CMD_KGDB) {
if (KDB_STATE(DOING_KGDB) || KDB_STATE(DOING_KGDB2)) {
Expand Down

0 comments on commit f5316b4

Please sign in to comment.