From 9f1ea45e416eb6b1fc880713f730d9870ea2634c Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Sat, 5 Mar 2016 17:35:06 -0800 Subject: [PATCH] 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. --- riscv/gdbserver.cc | 251 ++++++++++++++++++++++++++++++++++++++++++++ riscv/gdbserver.h | 70 ++++++++++++ riscv/riscv.mk.in | 2 + riscv/sim.cc | 3 + riscv/sim.h | 3 + spike_main/spike.cc | 3 + 6 files changed, 332 insertions(+) create mode 100644 riscv/gdbserver.cc create mode 100644 riscv/gdbserver.h diff --git a/riscv/gdbserver.cc b/riscv/gdbserver.cc new file mode 100644 index 0000000..83587f9 --- /dev/null +++ b/riscv/gdbserver.cc @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gdbserver.h" + +template +unsigned int circular_buffer_t::size() const +{ + if (end >= start) + return end - start; + else + return end + capacity - start; +} + +template +void circular_buffer_t::consume(unsigned int bytes) +{ + start = (start + bytes) % capacity; +} + +template +unsigned int circular_buffer_t::contiguous_space() const +{ + if (end >= start) + if (start == 0) + return capacity - end - 1; + else + return capacity - end; + else + return start - end - 1; +} + +template +void circular_buffer_t::data_added(unsigned int bytes) +{ + end += bytes; + assert(end <= capacity); + if (end == capacity) + end = 0; +} + +template +void circular_buffer_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 &packet) +{ + for (uint8_t c : packet) { + fprintf(stderr, "%c", c); + } + fprintf(stderr, "\n"); +} + +uint8_t compute_checksum(const std::vector &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 &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 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 $# + // where 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 &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; +} diff --git a/riscv/gdbserver.h b/riscv/gdbserver.h new file mode 100644 index 0000000..c947936 --- /dev/null +++ b/riscv/gdbserver.h @@ -0,0 +1,70 @@ +#ifndef _RISCV_GDBSERVER_H +#define _RISCV_GDBSERVER_H + +#include + +template +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 &packet); + +private: + int socket_fd; + int client_fd; + circular_buffer_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 diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index 18d91c5..c7d84f7 100644 --- a/riscv/riscv.mk.in +++ b/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 = diff --git a/riscv/sim.cc b/riscv/sim.cc index db08cb2..f8564fb 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -68,6 +68,9 @@ int sim_t::run() interactive(); else step(INTERLEAVE); + if (gdbserver) { + gdbserver->handle(); + } } return htif->exit_code(); } diff --git a/riscv/sim.h b/riscv/sim.h index af2d910..cac1daf 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -8,6 +8,7 @@ #include #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) { diff --git a/spike_main/spike.cc b/spike_main/spike.cc index 2f88518..b7748d6 100644 --- a/spike_main/spike.cc +++ b/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 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()); -- 2.30.2