Browse Source

Listen on a socket for gdb to connect to.

So far it just listens, and gdb times out because it's not getting any
messages back.

Receive packets and verify their checksum.
pull/39/head
Tim Newsome 10 years ago
parent
commit
9f1ea45e41
  1. 251
      riscv/gdbserver.cc
  2. 70
      riscv/gdbserver.h
  3. 2
      riscv/riscv.mk.in
  4. 3
      riscv/sim.cc
  5. 3
      riscv/sim.h
  6. 3
      spike_main/spike.cc

251
riscv/gdbserver.cc

@ -0,0 +1,251 @@
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <cassert>
#include <cstdio>
#include <vector>
#include "gdbserver.h"
template <typename T>
unsigned int circular_buffer_t<T>::size() const
{
if (end >= start)
return end - start;
else
return end + capacity - start;
}
template <typename T>
void circular_buffer_t<T>::consume(unsigned int bytes)
{
start = (start + bytes) % capacity;
}
template <typename T>
unsigned int circular_buffer_t<T>::contiguous_space() const
{
if (end >= start)
if (start == 0)
return capacity - end - 1;
else
return capacity - end;
else
return start - end - 1;
}
template <typename T>
void circular_buffer_t<T>::data_added(unsigned int bytes)
{
end += bytes;
assert(end <= capacity);
if (end == capacity)
end = 0;
}
template <typename T>
void circular_buffer_t<T>::reset()
{
start = 0;
end = 0;
}
// Code inspired by/copied from OpenOCD server/server.c.
gdbserver_t::gdbserver_t(uint16_t port) :
recv_buf(64 * 1024),
send_start(0), send_end(0)
{
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd == -1) {
fprintf(stderr, "error creating socket: %s\n", strerror(errno));
abort();
}
int so_reuseaddr_option = 1;
setsockopt(socket_fd,
SOL_SOCKET,
SO_REUSEADDR,
(void *)&so_reuseaddr_option,
sizeof(int));
int oldopts = fcntl(socket_fd, F_GETFL, 0);
fcntl(socket_fd, F_SETFL, oldopts | O_NONBLOCK);
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(port);
if (bind(socket_fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
fprintf(stderr, "couldn't bind to socket: %s\n", strerror(errno));
abort();
}
/* These setsockopt()s must happen before the listen() */
int window_size = 128 * 1024;
setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF,
(char *)&window_size, sizeof(window_size));
setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF,
(char *)&window_size, sizeof(window_size));
if (listen(socket_fd, 1) == -1) {
fprintf(stderr, "couldn't listen on socket: %s\n", strerror(errno));
abort();
}
}
void gdbserver_t::accept()
{
struct sockaddr client_addr;
socklen_t address_size = sizeof(client_addr);
client_fd = ::accept(socket_fd, &client_addr, &address_size);
if (client_fd == -1) {
if (errno == EAGAIN) {
// We'll try again in the next call.
} else {
fprintf(stderr, "failed to accept on socket: %s (%d)\n", strerror(errno), errno);
abort();
}
} else {
int oldopts = fcntl(client_fd, F_GETFL, 0);
fcntl(client_fd, F_SETFL, oldopts | O_NONBLOCK);
ack_mode = true;
}
}
void gdbserver_t::read()
{
// Reading from a non-blocking socket still blocks if there is no data
// available.
size_t count = recv_buf.contiguous_space();
assert(count > 0);
ssize_t bytes = ::read(client_fd, recv_buf.contiguous_data(), count);
if (bytes == -1) {
if (errno == EAGAIN) {
// We'll try again the next call.
} else {
fprintf(stderr, "failed to read on socket: %s (%d)\n", strerror(errno), errno);
abort();
}
} else if (bytes == 0) {
// The remote disconnected.
client_fd = 0;
recv_buf.reset();
send_start = 0;
send_end = 0;
} else {
printf("read %ld bytes\n", bytes);
recv_buf.data_added(bytes);
}
}
void print_packet(const std::vector<uint8_t> &packet)
{
for (uint8_t c : packet) {
fprintf(stderr, "%c", c);
}
fprintf(stderr, "\n");
}
uint8_t compute_checksum(const std::vector<uint8_t> &packet)
{
uint8_t checksum = 0;
for (auto i = packet.begin() + 1; i != packet.end() - 3; i++ ) {
checksum += *i;
}
return checksum;
}
uint8_t character_hex_value(uint8_t character)
{
if (character >= '0' && character <= '9')
return character - '0';
if (character >= 'a' && character <= 'f')
return 10 + character - 'a';
if (character >= 'A' && character <= 'F')
return 10 + character - 'A';
return 0;
}
uint8_t extract_checksum(const std::vector<uint8_t> &packet)
{
return character_hex_value(*(packet.end() - 1)) +
16 * character_hex_value(*(packet.end() - 2));
}
void gdbserver_t::process_requests()
{
// See https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html
while (!recv_buf.empty()) {
std::vector<uint8_t> packet;
for (unsigned int i = 0; i < recv_buf.size(); i++) {
uint8_t b = recv_buf[i];
if (b == '$') {
// Start of new packet.
if (!packet.empty()) {
fprintf(stderr, "Received malformed %ld-byte packet from debug client\n", packet.size());
print_packet(packet);
recv_buf.consume(i);
break;
}
}
packet.push_back(b);
// Packets consist of $<packet-data>#<checksum>
// where <checksum> is
if (packet.size() >= 4 &&
packet[packet.size()-3] == '#') {
if (compute_checksum(packet) == extract_checksum(packet)) {
handle_packet(packet);
} else {
fprintf(stderr, "Received %ld-byte packet with invalid checksum\n", packet.size());
fprintf(stderr, "Computed checksum: %x\n", compute_checksum(packet));
print_packet(packet);
send("-");
}
recv_buf.consume(i+1);
break;
}
}
// There's a partial packet in the buffer. Wait until we get more data to
// process it.
if (packet.size())
break;
}
}
void gdbserver_t::handle_packet(const std::vector<uint8_t> &packet)
{
fprintf(stderr, "Received %ld-byte packet from debug client\n", packet.size());
print_packet(packet);
send("+");
}
void gdbserver_t::handle()
{
if (client_fd > 0) {
this->read();
} else {
this->accept();
}
this->process_requests();
}
void gdbserver_t::send(const char* msg)
{
unsigned int length = strlen(msg);
unsigned int count;
}

70
riscv/gdbserver.h

@ -0,0 +1,70 @@
#ifndef _RISCV_GDBSERVER_H
#define _RISCV_GDBSERVER_H
#include <stdint.h>
template <typename T>
class circular_buffer_t
{
public:
// The buffer can store capacity-1 data elements.
circular_buffer_t(unsigned int capacity) : data(new T[capacity]),
start(0), end(0), capacity(capacity) {}
circular_buffer_t() : start(0), end(0), capacity(0) {}
~circular_buffer_t() { delete data; }
T *data;
unsigned int start; // Data start, inclusive.
unsigned int end; // Data end, exclusive.
unsigned int capacity; // Size of the buffer.
unsigned int size() const;
bool empty() const { return start == end; }
// Tell the buffer that some bytes were consumed from the start of the
// buffer.
void consume(unsigned int bytes);
// Return size and address of the block of RAM where more data can be copied
// to be added to the buffer.
unsigned int contiguous_space() const;
T *contiguous_data() { return data + end; }
void data_added(unsigned int bytes);
void reset();
T operator[](unsigned int i) {
return data[(start + i) % capacity];
}
};
class gdbserver_t
{
public:
// Create a new server, listening for connections from localhost on the given
// port.
gdbserver_t(uint16_t port);
// Process all pending messages from a client.
void handle();
void handle_packet(const std::vector<uint8_t> &packet);
private:
int socket_fd;
int client_fd;
circular_buffer_t<uint8_t> recv_buf;
uint8_t send_buf[64 * 1024]; // Circular buffer.
unsigned int send_start, send_end; // Data start (inclusive)/end (exclusive)pointers.
bool ack_mode;
// Read pending data from the client.
void read();
// Accept a new client if there isn't one already connected.
void accept();
// Process all complete requests in recv_buf.
void process_requests();
// Add the given message to send_buf.
void send(const char* msg);
};
#endif

2
riscv/riscv.mk.in

@ -24,6 +24,7 @@ riscv_hdrs = \
rocc.h \
insn_template.h \
mulhi.h \
gdbserver.h \
riscv_precompiled_hdrs = \
insn_template.h \
@ -45,6 +46,7 @@ riscv_srcs = \
devices.cc \
rom.cc \
rtc.cc \
gdbserver.cc \
$(riscv_gen_srcs) \
riscv_test_srcs =

3
riscv/sim.cc

@ -68,6 +68,9 @@ int sim_t::run()
interactive();
else
step(INTERLEAVE);
if (gdbserver) {
gdbserver->handle();
}
}
return htif->exit_code();
}

3
riscv/sim.h

@ -8,6 +8,7 @@
#include <memory>
#include "processor.h"
#include "devices.h"
#include "gdbserver.h"
class htif_isasim_t;
class mmu_t;
@ -27,6 +28,7 @@ public:
void set_log(bool value);
void set_histogram(bool value);
void set_procs_debug(bool value);
void set_gdbserver(gdbserver_t* gdbserver) { this->gdbserver = gdbserver; }
htif_isasim_t* get_htif() { return htif.get(); }
const char* get_config_string() { return config_string.c_str(); }
@ -54,6 +56,7 @@ private:
bool debug;
bool log;
bool histogram_enabled; // provide a histogram of PCs
gdbserver_t* gdbserver;
// memory-mapped I/O routines
bool addr_is_mem(reg_t addr) {

3
spike_main/spike.cc

@ -2,6 +2,7 @@
#include "sim.h"
#include "mmu.h"
#include "gdbserver.h"
#include "htif.h"
#include "cachesim.h"
#include "extension.h"
@ -73,6 +74,8 @@ int main(int argc, char** argv)
auto argv1 = parser.parse(argv);
std::vector<std::string> htif_args(argv1, (const char*const*)argv + argc);
sim_t s(isa, nprocs, mem_mb, htif_args);
gdbserver_t gdbserver(9824);
s.set_gdbserver(&gdbserver);
if (dump_config_string) {
printf("%s", s.get_config_string());

Loading…
Cancel
Save