/*
* 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@suse.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/kgdb.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];
/* Storage for the registers, in GDB format. */
static unsigned long gdb_regs[(NUMREGBYTES +
sizeof(unsigned long) - 1) /
sizeof(unsigned long)];
/*
* GDB remote protocol parser:
*/
static int hex(char ch)
{
if ((ch >= 'a') && (ch <= 'f'))
return ch - 'a' + 10;
if ((ch >= '0') && (ch <= '9'))
return ch - '0';
if ((ch >= 'A') && (ch <= 'F'))
return ch - 'A' + 10;
return -1;
}
/* 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 = (dbg_io_ops->read_char())) != '$')
/* 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 = dbg_io_ops->read_char();
if (ch == '#')
break;
checksum = checksum + ch;
buffer[count] = ch;
count = count + 1;
}
buffer[count] = 0;
if (ch == '#') {
xmitcsum = hex(dbg_io_ops->read_char()) << 4;
xmitcsum += hex(dbg_io_ops->read_char());
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();
}
} while (checksum != xmitcsum);
}
/*
* Send the packet in buffer.
* Check for gdb connection if asked for.
*/
static void put_packet(char *buffer)
{
unsigned char checksum;
int count;
char ch;
/*
* $<packet info>#<checksum>.
*/
while (1) {
dbg_io_ops->write_char('$');
checksum = 0;
count = 0;
while ((ch = buffer[count])) {
dbg_io_ops->write_char(ch);
checksum += ch;
count++;
}
dbg_io_ops->write_char('#');
dbg_io_ops->write_char(hex_asc_hi(checksum));
dbg_io_ops->write_char(hex_asc_lo(checksum));
if (dbg_io_ops->flush)
dbg_io_ops->flush();
/* Now see what we get in reply. */
ch = dbg_io_ops->read_char();
if (ch == 3)
ch = dbg_io_ops->read_char();
/* If we get an ACK, we are done. */
if (ch == '+')
return;
/*
* If we get the start of another packet, this means
* that GDB is attempting to reconnect. We will NAK
* the packet being sent, and stop trying to send this
* packet.
*/
if (ch == '$') {
dbg_io_ops->write_char('-');
if (dbg_io_ops->flush)
dbg_io_ops->flush();
return;
}
}
}
static char gdbmsgbuf[BUFMAX + 1];
void gdbstub_msg_write(const char *s, int len)
{
char *bufptr;
int wcount;
int i;
/* 'O'utput */
gdbmsgbuf[0] = 'O';
/* Fill and send buffers... */
while (len > 0) {
bufptr = gdbmsgbuf + 1;
/* Calculate how many this time */
if ((len << 1) > (BUFMAX - 2))
wcount = (BUFMAX - 2) >>