From 8f92087c5263801273b9a1c1cf600eedcecc036a Mon Sep 17 00:00:00 2001 From: Shivam Aggarwal Date: Wed, 27 Jun 2018 01:54:41 +0530 Subject: [PATCH] OSD-GDB server: Adds following features (1) Connect to GDB over TCP (2) Send and Recieve data to/from the client (3) Receive RSP packet from the client (4) Validate the obtained packet using checksum (5) Send RSP packet to the client --- src/libosd/include/osd/osd_gdbserver.h | 123 ++++++++++ src/libosd/osd_gdbserver.c | 299 +++++++++++++++++++++++++ 2 files changed, 422 insertions(+) create mode 100644 src/libosd/include/osd/osd_gdbserver.h create mode 100644 src/libosd/osd_gdbserver.c diff --git a/src/libosd/include/osd/osd_gdbserver.h b/src/libosd/include/osd/osd_gdbserver.h new file mode 100644 index 0000000..cb02eb6 --- /dev/null +++ b/src/libosd/include/osd/osd_gdbserver.h @@ -0,0 +1,123 @@ +/* Copyright 2018 The Open SoC Debug Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OSD_GDBSERVER_H +#define OSD_GDBSERVER_H + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup libosd-gdbserver OSD-GDB server utility + * @ingroup libosd + * + * @{ + */ + +struct connection; + +/** + * Connect GDB server with GDB + * + * @return OSD_OK on success, any other value indicates an error + */ +osd_result add_connection(char *name, char *port); + +/** + * Read a character from the obtained buffer + */ +osd_result get_char(struct connection *c, int client_fd, int *ch); + +/** + * Read data from the GDB client + * + * @param c the connection object + * @param client_fd the client_fd of the GDB + * + * @return OSD_OK on success, any other value indicates an error + * + * @see put_data() + */ +osd_result get_data(struct connection *c, int client_fd); + +/** + * Write data to the GDB client + * + * @param c the connection object + * @param client_fd the client_fd of the GDB + * @param data the data to write to the connected client + * @param len the length of the data to be written + * @return OSD_OK on success, any other value indicates an error + * + * @see get_data() + */ +osd_result put_data(struct connection *c, int client_fd, char *data, int len); + +/** + * + * Get the packet-data from obtained buffer $# + * + * @param c the connection object + * @param client_fd the client_fd of the GDB + * @param buffer the packet-data obtained from the connected client + * @param len the length of the packet-data + * @return OSD_OK on success, any other value indicates an error + * + * @see put_packet() + */ +osd_result get_packet(struct connection *c, int client_fd, char *buffer, + int *len); + +/** + * Verify the checksum of obtained packet + * + * @param c the connection object + * @param client_fd the client_fd of the GDB + * @param ver_checksum indicates if the obtained checksum is correct or not + * @param buffer the packet-data obtained from the connected client + * @param len the length of the packet-data + * @return OSD_OK on success, any other value indicates an error + */ +osd_result validate_packet(struct connection *c, int client_fd, + bool *ver_checksum, int *len, char *buffer); + +/** + * + * Send the packet-data to the buffer $# + * + * @param c the connection object + * @param client_fd the client_fd of the GDB + * @param buffer the packet-data to send to the connected client + * @param len the length of the packet-data + * @return OSD_OK on success, any other value indicates an error + * + * @see get_packet() + */ +osd_result put_packet(struct connection *c, int client_fd, char *buffer, + int len); + +/**@}*/ /* end of doxygen group libosd-gdbserver */ + +#ifdef __cplusplus +} +#endif + +#endif // OSD_GDBSERVER_H diff --git a/src/libosd/osd_gdbserver.c b/src/libosd/osd_gdbserver.c new file mode 100644 index 0000000..4f207e5 --- /dev/null +++ b/src/libosd/osd_gdbserver.c @@ -0,0 +1,299 @@ +/* Copyright 2018 The Open SoC Debug Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "osd-private.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SERVER_PORT 5555 +#define BUFF_SIZE 1024 + +int dectohex(int packet_char); +void free_connection(struct connection *c); + +struct connection { + int fd; + char *name; + char *port; + struct sockaddr_in sin; + char buffer[BUFF_SIZE]; + int buf_cnt; + char *buf_p; + int closed; +}; + +osd_result add_connection(char *name, char *port) +{ + int sockoptval = 1; + struct connection *c = calloc(1, sizeof(struct connection)); + assert(c); + + c->name = strdup(name); + c->port = strdup(port); + c->fd = socket(AF_INET, SOCK_STREAM, 0); + + if (OSD_FAILED(c->fd)) { + free_connection(c); + return OSD_ERROR_CONNECTION_FAILED; + } + + setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(int)); + + memset(&c->sin, 0, sizeof(c->sin)); + c->sin.sin_family = AF_INET; + c->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + c->sin.sin_port = htons(SERVER_PORT); + + if (OSD_FAILED(bind(c->fd, (struct sockaddr *)&c->sin, sizeof(c->sin)))) { + close(c->fd); + free_connection(c); + return OSD_ERROR_CONNECTION_FAILED; + } + + if (OSD_FAILED(listen(c->fd, 1))) { + close(c->fd); + free_connection(c); + return OSD_ERROR_CONNECTION_FAILED; + } + + int client_fd; + struct sockaddr_in addr_in; + addr_in.sin_port = 0; + socklen_t addr_in_size = sizeof(addr_in); + + getsockname(c->fd, (struct sockaddr *)&addr_in, &addr_in_size); + printf("server started on %s, listening on port %d\n", name, + ntohs(addr_in.sin_port)); + + while (1) { + client_fd = accept(c->fd, (struct sockaddr *)&addr_in, &addr_in_size); + if (OSD_FAILED(client_fd)) { + int rev = close(client_fd); + if (OSD_SUCCEEDED(rev)) { + break; + } + } + printf("Server got connection from client %s\n", + inet_ntoa(addr_in.sin_addr)); + + if (OSD_SUCCEEDED(close(client_fd))) { + break; + } + } + + close(c->fd); + + return OSD_OK; +} + +osd_result get_data(struct connection *c, int client_fd) +{ + memset(c->buffer, 0, sizeof c->buffer); + c->buf_cnt = read(client_fd, c->buffer, BUFF_SIZE); + + if (OSD_FAILED(c->buf_cnt)) { + return OSD_ERROR_CONNECTION_FAILED; + } else { + if (c->buf_cnt > 0) { + printf("Server:Packet Received %s\n", c->buffer); + printf("Size of Packet:%d\n", c->buf_cnt); + return OSD_OK; + } + if (c->buf_cnt == 0) { + c->closed = 1; + return OSD_ERROR_FAILURE; + } + } + + return OSD_OK; +} + +osd_result put_data(struct connection *c, int client_fd, char *data, int len) +{ + if (c->closed == 1) { + return OSD_ERROR_NOT_CONNECTED; + } + int wlen = write(client_fd, data, len); + if (wlen == len) { + return OSD_OK; + } + + return OSD_ERROR_NOT_CONNECTED; +} + +osd_result get_char(struct connection *c, int client_fd, int *ch) +{ + osd_result rv; + + c->buf_p = c->buffer; + c->buf_cnt--; + if (OSD_FAILED(c->buf_cnt)) { + return OSD_ERROR_FAILURE; + } + *ch = *(c->buf_p++); + + return OSD_OK; +} + +osd_result validate_packet(struct connection *c, int client_fd, + bool *ver_checksum, int *len, char *buffer) +{ + unsigned char val_checksum = 0; + char packet_checksum[3]; + int packet_char; + int cnt = 0; + osd_result rv; + + char *buf_p = c->buf_p; + int buf_cnt = c->buf_cnt; + + // packet-format: $packet-data#checksum + int i = 0; + char *buf = buf_p; + int done = 0; + // traversing through the obtained packet till we obtained '#' + while (1) { + packet_char = *buf++; + i++; + + if (packet_char == '#') { + done = 1; + break; + } + /*Any escaped byte (here, '}') is transmitted as the escape + * character followed by the original character XORed with 0x20. + */ + if (packet_char == '}') { + val_checksum += packet_char & 0xff; + packet_char = *buf++; + i++; + val_checksum += packet_char & 0xff; + buffer[cnt++] = (packet_char ^ 0x20) & 0xff; + } else { + val_checksum += packet_char & 0xff; + buffer[cnt++] = packet_char & 0xff; + } + } + + *len = cnt; + packet_char = *buf++; + packet_checksum[0] = packet_char; + packet_char = *buf; + packet_checksum[1] = packet_char; + packet_checksum[2] = 0; + *ver_checksum = (val_checksum == strtoul(packet_checksum, NULL, 16)); + + return OSD_OK; +} + +osd_result get_packet(struct connection *c, int client_fd, char *buffer, + int *len) +{ + int packet_char; + osd_result rv; + + do { + rv = get_char(c, client_fd, &packet_char); + if (OSD_FAILED(rv)) { + return rv; + } + } while (packet_char != '$'); + + bool ver_checksum = 0; + rv = validate_packet(c, client_fd, &ver_checksum, len, buffer); + + if (OSD_FAILED(rv)) { + return rv; + } else { + if (ver_checksum == 1) { + rv = put_data(c, client_fd, "+", 1); + } else { + rv = put_data(c, client_fd, "-", 1); + } + if (OSD_FAILED(rv)) { + return rv; + } + } + return OSD_OK; +} + +osd_result put_packet(struct connection *c, int client_fd, char *buffer, + int len) +{ + char packet_buffer[len + 3]; + int packet_checksum = 0; + osd_result rv; + + while (1) { + packet_buffer[0] = '$'; + memcpy(packet_buffer + 1, buffer, len); + int j = len + 1; + packet_buffer[j++] = '#'; + for (int i = 0; i < len; i++) { + packet_checksum += buffer[i]; + } + packet_buffer[j++] = dectohex((packet_checksum >> 4) & 0xf); + packet_buffer[j] = dectohex(packet_checksum & 0xf); + + rv = put_data(c, client_fd, packet_buffer, len + 4); + if (OSD_FAILED(rv)) { + return OSD_ERROR_FAILURE; + } + + rv = get_data(c, client_fd); + if (OSD_FAILED(rv)) { + return OSD_ERROR_FAILURE; + } + + char reply = c->buffer[0]; + if (reply == '+') { + break; + } else { + return OSD_ERROR_FAILURE; + } + } + + return OSD_OK; +} + +int dectohex(int packet_char) +{ + if (packet_char < 10) { + return packet_char + '0'; + } else { + return packet_char - 10 + 'a'; + } +} + +void free_connection(struct connection *c) +{ + free(c->name); + free(c->port); + free(c); +}