Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[GDB] Remote debugging over UDP
This commit implements GDB over UDP.  Using UDP is more complex than
serial and has required some restructuring.

The GDB stub is now built using one or both of GDBSERIAL and GDBUDP
config.h options.

To enter the debugger, execute the gPXE shell command:
gdbstub <transport> [<options>...]

Where <transport> is "serial" or "udp".  For "udp", the name of a
configured network device is required:
gdbstub udp net0

The GDB stub listens on UDP port 43770 by default.
  • Loading branch information
stefanha authored and Michael Brown committed Jun 30, 2008
1 parent 9ec3ff9 commit 6e670b5
Show file tree
Hide file tree
Showing 8 changed files with 501 additions and 50 deletions.
4 changes: 3 additions & 1 deletion src/config.h
Expand Up @@ -163,7 +163,9 @@
#undef BUILD_ID /* Include a custom build ID string,
* e.g "test-foo" */
#undef NULL_TRAP /* Attempt to catch NULL function calls */
#undef GDBSTUB /* Remote GDB debugging */
#undef GDBSERIAL /* Remote GDB debugging over serial */
#undef GDBUDP /* Remote GDB debugging over UDP
* (both may be set) */

/* @END general.h */

Expand Down
9 changes: 8 additions & 1 deletion src/core/config.c
Expand Up @@ -195,6 +195,13 @@ REQUIRE_OBJECT ( sanboot_cmd );
#ifdef NULL_TRAP
REQUIRE_OBJECT ( nulltrap );
#endif
#ifdef GDBSTUB
#ifdef GDBSERIAL
REQUIRE_OBJECT ( gdbidt );
REQUIRE_OBJECT ( gdbserial );
REQUIRE_OBJECT ( gdbstub_cmd );
#endif
#ifdef GDBUDP
REQUIRE_OBJECT ( gdbidt );
REQUIRE_OBJECT ( gdbudp );
REQUIRE_OBJECT ( gdbstub_cmd );
#endif
41 changes: 41 additions & 0 deletions src/core/gdbserial.c
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <assert.h>
#include <gpxe/serial.h>
#include <gpxe/gdbstub.h>

struct gdb_transport serial_gdb_transport __gdb_transport;

static size_t gdbserial_recv ( char *buf, size_t len ) {
assert ( len > 0 );
buf [ 0 ] = serial_getc();
return 1;
}

static void gdbserial_send ( const char *buf, size_t len ) {
while ( len-- > 0 ) {
serial_putc ( *buf++ );
}
}

struct gdb_transport serial_gdb_transport __gdb_transport = {
.name = "serial",
.recv = gdbserial_recv,
.send = gdbserial_send,
};
89 changes: 47 additions & 42 deletions src/core/gdbstub.c
Expand Up @@ -24,33 +24,40 @@
*/

#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <byteswap.h>
#include <gpxe/process.h>
#include <gpxe/serial.h>
#include <gpxe/gdbstub.h>
#include "gdbmach.h"

enum {
POSIX_EINVAL = 0x1c /* used to report bad arguments to GDB */
POSIX_EINVAL = 0x1c, /* used to report bad arguments to GDB */
SIZEOF_PAYLOAD = 256, /* buffer size of GDB payload data */
};

struct gdbstub {
struct gdb_transport *trans;
int exit_handler; /* leave interrupt handler */

int signo;
gdbreg_t *regs;
int exit_handler; /* leave interrupt handler */

void ( * parse ) ( struct gdbstub *stub, char ch );
uint8_t cksum1;

/* Buffer for payload data when parsing a packet. Once the
* packet has been received, this buffer is used to hold
* the reply payload. */
char payload [ 256 ];
int len;
char buf [ SIZEOF_PAYLOAD + 4 ]; /* $...PAYLOAD...#XX */
char *payload; /* start of payload */
int len; /* length of payload */
};

/* Transports */
static struct gdb_transport gdb_transport_start[0] __table_start ( struct gdb_transport, gdb_transports );
static struct gdb_transport gdb_transport_end[0] __table_end ( struct gdb_transport, gdb_transports );

/* Packet parser states */
static void gdbstub_state_new ( struct gdbstub *stub, char ch );
static void gdbstub_state_data ( struct gdbstub *stub, char ch );
Expand Down Expand Up @@ -132,29 +139,13 @@ static uint8_t gdbstub_cksum ( char *data, int len ) {
return cksum;
}

static int gdbstub_getchar ( struct gdbstub *stub ) {
if ( stub->exit_handler ) {
return -1;
}
return serial_getc();
}

static void gdbstub_putchar ( struct gdbstub * stub __unused, char ch ) {
serial_putc ( ch );
}

static void gdbstub_tx_packet ( struct gdbstub *stub ) {
uint8_t cksum = gdbstub_cksum ( stub->payload, stub->len );
int i;

gdbstub_putchar ( stub, '$' );
for ( i = 0; i < stub->len; i++ ) {
gdbstub_putchar ( stub, stub->payload [ i ] );
}
gdbstub_putchar ( stub, '#' );
gdbstub_putchar ( stub, gdbstub_to_hex_digit ( cksum >> 4 ) );
gdbstub_putchar ( stub, gdbstub_to_hex_digit ( cksum ) );

stub->buf [ 0 ] = '$';
stub->buf [ stub->len + 1 ] = '#';
stub->buf [ stub->len + 2 ] = gdbstub_to_hex_digit ( cksum >> 4 );
stub->buf [ stub->len + 3 ] = gdbstub_to_hex_digit ( cksum );
stub->trans->send ( stub->buf, stub->len + 4 );
stub->parse = gdbstub_state_wait_ack;
}

Expand Down Expand Up @@ -229,7 +220,7 @@ static void gdbstub_read_mem ( struct gdbstub *stub ) {
gdbstub_send_errno ( stub, POSIX_EINVAL );
return;
}
args [ 1 ] = ( args [ 1 ] < sizeof stub->payload / 2 ) ? args [ 1 ] : sizeof stub->payload / 2;
args [ 1 ] = ( args [ 1 ] < SIZEOF_PAYLOAD / 2 ) ? args [ 1 ] : SIZEOF_PAYLOAD / 2;
gdbstub_to_hex_buf ( stub->payload, ( char * ) args [ 0 ], args [ 1 ] );
stub->len = args [ 1 ] * 2;
gdbstub_tx_packet ( stub );
Expand Down Expand Up @@ -306,7 +297,7 @@ static void gdbstub_state_data ( struct gdbstub *stub, char ch ) {
stub->len = 0; /* retry new packet */
} else {
/* If the length exceeds our buffer, let the checksum fail */
if ( stub->len < ( int ) sizeof stub->payload ) {
if ( stub->len < SIZEOF_PAYLOAD ) {
stub->payload [ stub->len++ ] = ch;
}
}
Expand All @@ -325,12 +316,12 @@ static void gdbstub_state_cksum2 ( struct gdbstub *stub, char ch ) {
their_cksum = stub->cksum1 + gdbstub_from_hex_digit ( ch );
our_cksum = gdbstub_cksum ( stub->payload, stub->len );
if ( their_cksum == our_cksum ) {
gdbstub_putchar ( stub, '+' );
stub->trans->send ( "+", 1 );
if ( stub->len > 0 ) {
gdbstub_rx_packet ( stub );
}
} else {
gdbstub_putchar ( stub, '-' );
stub->trans->send ( "-", 1 );
}
}

Expand All @@ -351,23 +342,37 @@ static struct gdbstub stub = {
};

__cdecl void gdbstub_handler ( int signo, gdbreg_t *regs ) {
int ch;
char packet [ SIZEOF_PAYLOAD + 4 ];
size_t len, i;

/* A transport must be set up */
if ( !stub.trans ) {
return;
}

stub.signo = signo;
stub.regs = regs;
stub.exit_handler = 0;
gdbstub_report_signal ( &stub );
while ( ( ch = gdbstub_getchar( &stub ) ) != -1 ) {
gdbstub_parse ( &stub, ch );
while ( !stub.exit_handler && ( len = stub.trans->recv ( packet, sizeof ( packet ) ) ) > 0 ) {
for ( i = 0; i < len; i++ ) {
gdbstub_parse ( &stub, packet [ i ] );
}
}
}

/* Activity monitor to detect packets from GDB when we are not active */
static void gdbstub_activity_step ( struct process *process __unused ) {
if ( serial_ischar() ) {
gdbmach_breakpoint();
struct gdb_transport *find_gdb_transport ( const char *name ) {
struct gdb_transport *trans;
for ( trans = gdb_transport_start; trans < gdb_transport_end; trans++ ) {
if ( strcmp ( trans->name, name ) == 0 ) {
return trans;
}
}
return NULL;
}

struct process gdbstub_activity_process __permanent_process = {
.step = gdbstub_activity_step,
};
void gdbstub_start ( struct gdb_transport *trans ) {
stub.trans = trans;
stub.payload = &stub.buf [ 1 ];
gdbmach_breakpoint();
}

0 comments on commit 6e670b5

Please sign in to comment.