/*
* Kernel Debug Core
*
* Maintainer: Jason Wessel <jason.wessel@windriver.com>
*
* Copyright (C) 2000-2001 VERITAS Software Corporation.
* Copyright (C) 2002-2004 Timesys Corporation
* Copyright (C) 2003-2004 Amit S. Kale <amitkale@linsyssoft.com>
* Copyright (C) 2004 Pavel Machek <pavel@ucw.cz>
* Copyright (C) 2004-2006 Tom Rini <trini@kernel.crashing.org>
* Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd.
* Copyright (C) 2005-2009 Wind River Systems, Inc.
* Copyright (C) 2007 MontaVista Software, Inc.
* Copyright (C) 2008 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
*
* Contributors at various stages not listed above:
* Jason Wessel ( jason.wessel@windriver.com )
* George Anzinger <george@mvista.com>
* Anurekh Saxena (anurekh.saxena@timesys.com)
* Lake Stevens Instrument Division (Glenn Engel)
* Jim Kingdon, Cygnus Support.
*
* Original KGDB stub: David Grothe <dave@gcom.com>,
* Tigran Aivazian <tigran@sco.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/sched/signal.h>
#include <linux/kgdb.h>
#include <linux/kdb.h>
#include <linux/serial_core.h>
#include <linux/reboot.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
#include <asm/unaligned.h>
#include "debug_core.h"
#define KGDB_MAX_THREAD_QUERY 17
/* Our I/O buffers. */
static char remcom_in_buffer[BUFMAX];
static char remcom_out_buffer[BUFMAX];
static int gdbstub_use_prev_in_buf;
static int gdbstub_prev_in_buf_pos;
/* Storage for the registers, in GDB format. */
static unsigned long gdb_regs[(NUMREGBYTES +
sizeof(unsigned long) - 1) /
sizeof(unsigned long)];
/*
* GDB remote protocol parser:
*/
#ifdef CONFIG_KGDB_KDB
static int gdbstub_read_wait(void)
{
int ret = -1;
int i;
if (unlikely(gdbstub_use_prev_in_buf)) {
if (gdbstub_prev_in_buf_pos < gdbstub_use_prev_in_buf)
return remcom_in_buffer[gdbstub_prev_in_buf_pos++];
else
gdbstub_use_prev_in_buf = 0;
}
/* 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)
{
unsigned char checksum;
unsigned char xmitcsum;
int count;
char ch;
do {
/*
* Spin and wait around for the start character, ignore all
* other characters:
*/
while ((ch = (gdbstub_read_wait())) != '$')
/* nothing */;
kgdb_connected = 1;
checksum = 0;
xmitcsum = -1;
count = 0;
/*
* now, read until a # or end of buffer is found:
*/
while (count < (BUFMAX - 1)) {
ch = gdbstub_read_wait();
if (ch == '#')
break;
checksum = checksum + ch;
buffer[count] = ch;
count = count + 1;
}
if (ch == '#') {
xmitcsum = hex_to_bin(gdbstub_read_wait()) << 4;
xmitcsum += hex_to_bin(gdbstub_read_wait());
if (checksum != xmitcsum)
/* failed checksum */
dbg_io_ops->write_char('-');
else
/* successful transfer */
dbg_io_ops->write_char('+');
if (dbg_io_ops->flush)
dbg_io_ops->flush();
}
buffer[count] = 0;
} while (checksum != xmitcsum);
}
/*
* Send the packet in buffer.
* Check for gdb connection if asked for.
*/
static void