Browse Source
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
6 changed files with 332 additions and 0 deletions
@ -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; |
||||
|
} |
||||
@ -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 |
||||
Loading…
Reference in new issue