From b65770f8a5ff4bd984337d9a1ef1601e3eca82ce Mon Sep 17 00:00:00 2001 From: Shivam Aggarwal Date: Wed, 27 Jun 2018 01:54:41 +0530 Subject: [PATCH 1/7] 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/Makefile.am | 6 +- src/libosd/gdbserver.c | 360 +++++++++++++++++++++++++++++ src/libosd/include/osd/gdbserver.h | 97 ++++++++ 3 files changed, 461 insertions(+), 2 deletions(-) create mode 100644 src/libosd/gdbserver.c create mode 100644 src/libosd/include/osd/gdbserver.h diff --git a/src/libosd/Makefile.am b/src/libosd/Makefile.am index a43c01c..4c3da6b 100644 --- a/src/libosd/Makefile.am +++ b/src/libosd/Makefile.am @@ -13,7 +13,8 @@ pkginclude_HEADERS =\ include/osd/cl_cdm.h \ include/osd/memaccess.h \ include/osd/systracelogger.h \ - include/osd/coretracelogger.h + include/osd/coretracelogger.h \ + include/osd/gdbserver.h lib_LTLIBRARIES = libosd.la @@ -33,7 +34,8 @@ libosd_la_SOURCES =\ cl_cdm.c \ memaccess.c \ systracelogger.c \ - coretracelogger.c + coretracelogger.c \ + gdbserver.c libosd_la_CFLAGS = $(AM_CFLAGS) diff --git a/src/libosd/gdbserver.c b/src/libosd/gdbserver.c new file mode 100644 index 0000000..aa0a16f --- /dev/null +++ b/src/libosd/gdbserver.c @@ -0,0 +1,360 @@ +/* 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 +#include "osd-private.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * OSD-GDB server context + */ +struct osd_gdbserver_ctx { + struct osd_hostmod_ctx *hostmod_ctx; + struct osd_log_ctx *log_ctx; + int fd; + char *name; + char *port; + struct sockaddr_in sin; + char buffer[OSD_GDBSERVER_BUFF_SIZE]; + int buf_cnt; + char *buf_p; + int closed; + int client_fd; +}; + +API_EXPORT +osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, + struct osd_log_ctx *log_ctx, + const char *host_controller_address) +{ + osd_result rv; + + struct osd_gdbserver_ctx *c = calloc(1, sizeof(struct osd_gdbserver_ctx)); + assert(c); + + c->log_ctx = log_ctx; + + struct osd_hostmod_ctx *hostmod_ctx; + rv = osd_hostmod_new(&hostmod_ctx, log_ctx, host_controller_address, NULL, + NULL); + assert(OSD_SUCCEEDED(rv)); + c->hostmod_ctx = hostmod_ctx; + + *ctx = c; + + return OSD_OK; +} + +static int dectohex(int packet_char) +{ + if (packet_char < 10) { + return packet_char + '0'; + } else { + return packet_char - 10 + 'a'; + } +} + +static void free_service(struct osd_gdbserver_ctx *ctx) +{ + free(ctx->name); + free(ctx->port); + free(ctx); +} + +API_EXPORT +osd_result osd_gdbserver_connect(struct osd_gdbserver_ctx *ctx, char *name, + char *port) +{ + osd_result rv = osd_hostmod_connect(ctx->hostmod_ctx); + if (OSD_FAILED(rv)) { + return rv; + } + + int sockoptval = 1; + + ctx->name = strdup(name); + ctx->port = strdup(port); + ctx->fd = socket(AF_INET, SOCK_STREAM, 0); + + if (OSD_FAILED(ctx->fd)) { + free_service(ctx); + return OSD_ERROR_CONNECTION_FAILED; + } + + setsockopt(ctx->fd, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(int)); + + memset(&ctx->sin, 0, sizeof(ctx->sin)); + ctx->sin.sin_family = AF_INET; + ctx->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ctx->sin.sin_port = htons(OSD_GDBSERVER_PORT); + + if (OSD_FAILED( + bind(ctx->fd, (struct sockaddr *)&ctx->sin, sizeof(ctx->sin)))) { + close(ctx->fd); + free_service(ctx); + return OSD_ERROR_CONNECTION_FAILED; + } + + if (OSD_FAILED(listen(ctx->fd, 1))) { + close(ctx->fd); + free_service(ctx); + return OSD_ERROR_CONNECTION_FAILED; + } + + struct sockaddr_in addr_in; + addr_in.sin_port = 0; + socklen_t addr_in_size = sizeof(addr_in); + + getsockname(ctx->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) { + ctx->client_fd = + accept(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); + if (OSD_FAILED(ctx->client_fd)) { + rv = close(ctx->client_fd); + if (OSD_SUCCEEDED(rv)) { + break; + } + } + printf("Server got connection from client %s\n", + inet_ntoa(addr_in.sin_addr)); + + if (OSD_SUCCEEDED(close(ctx->client_fd))) { + break; + } + } + + close(ctx->fd); + + return OSD_OK; +} + +API_EXPORT +osd_result osd_gdbserver_disconnect(struct osd_gdbserver_ctx *ctx) +{ + return osd_hostmod_disconnect(ctx->hostmod_ctx); +} + +API_EXPORT +bool osd_gdbserver_is_connected(struct osd_gdbserver_ctx *ctx) +{ + return osd_hostmod_is_connected(ctx->hostmod_ctx); +} + +API_EXPORT +void osd_gdbserver_free(struct osd_gdbserver_ctx **ctx_p) +{ + assert(ctx_p); + struct osd_gdbserver_ctx *ctx = *ctx_p; + if (!ctx) { + return; + } + + osd_hostmod_free(&ctx->hostmod_ctx); + + free(ctx); + *ctx_p = NULL; +} + +API_EXPORT +osd_result osd_gdbserver_read_data(struct osd_gdbserver_ctx *ctx) +{ + memset(ctx->buffer, 0, sizeof ctx->buffer); + ctx->buf_cnt = read(ctx->client_fd, ctx->buffer, OSD_GDBSERVER_BUFF_SIZE); + + if (OSD_FAILED(ctx->buf_cnt)) { + return OSD_ERROR_CONNECTION_FAILED; + } else { + if (ctx->buf_cnt > 0) { + printf("Server:Packet Received %s\n", ctx->buffer); + printf("Size of Packet:%d\n", ctx->buf_cnt); + return OSD_OK; + } + if (ctx->buf_cnt == 0) { + ctx->closed = 1; + return OSD_ERROR_FAILURE; + } + } + + return OSD_OK; +} + +API_EXPORT +osd_result osd_gdbserver_write_data(struct osd_gdbserver_ctx *ctx, char *data, + int len) +{ + if (ctx->closed == 1) { + return OSD_ERROR_NOT_CONNECTED; + } + int wlen = write(ctx->client_fd, data, len); + if (wlen == len) { + return OSD_OK; + } + + return OSD_ERROR_NOT_CONNECTED; +} + +static osd_result get_char(struct osd_gdbserver_ctx *ctx, int *ch) +{ + osd_result rv; + + ctx->buf_p = ctx->buffer; + ctx->buf_cnt--; + if (OSD_FAILED(ctx->buf_cnt)) { + return OSD_ERROR_FAILURE; + } + *ch = *(ctx->buf_p++); + + return OSD_OK; +} + +static osd_result validate_rsp_packet(struct osd_gdbserver_ctx *ctx, + 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 = ctx->buf_p; + int buf_cnt = ctx->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; +} + +static osd_result receive_rsp_packet(struct osd_gdbserver_ctx *ctx, + char *buffer, int *len) +{ + int packet_char; + osd_result rv; + + do { + rv = get_char(ctx, &packet_char); + if (OSD_FAILED(rv)) { + return rv; + } + } while (packet_char != '$'); + + bool ver_checksum = 0; + rv = validate_rsp_packet(ctx, &ver_checksum, len, buffer); + + if (OSD_FAILED(rv)) { + return rv; + } else { + if (ver_checksum == 1) { + rv = osd_gdbserver_write_data(ctx, "+", 1); + } else { + rv = osd_gdbserver_write_data(ctx, "-", 1); + } + if (OSD_FAILED(rv)) { + return rv; + } + } + return OSD_OK; +} + +static osd_result send_rsp_packet(struct osd_gdbserver_ctx *ctx, 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 = osd_gdbserver_write_data(ctx, packet_buffer, len + 4); + if (OSD_FAILED(rv)) { + return OSD_ERROR_FAILURE; + } + + rv = osd_gdbserver_read_data(ctx); + if (OSD_FAILED(rv)) { + return OSD_ERROR_FAILURE; + } + + char reply = ctx->buffer[0]; + if (reply == '+') { + break; + } else { + return OSD_ERROR_FAILURE; + } + } + + return OSD_OK; +} diff --git a/src/libosd/include/osd/gdbserver.h b/src/libosd/include/osd/gdbserver.h new file mode 100644 index 0000000..3ca6df0 --- /dev/null +++ b/src/libosd/include/osd/gdbserver.h @@ -0,0 +1,97 @@ +/* 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 + +#define OSD_GDBSERVER_PORT 5555 +#define OSD_GDBSERVER_BUFF_SIZE 1024 + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup libosd-gdbserver OSD-GDB server utility + * @ingroup libosd + * + * @{ + */ + +struct osd_gdbserver_ctx; + +/** + * Create a new context object + */ +osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, + struct osd_log_ctx *log_ctx, + const char *host_controller_address); + +/** + * Connect GDB server to the host controller followed by GDB + * + * @return OSD_OK on success, any other value indicates an error + */ +osd_result osd_gdbserver_connect(struct osd_gdbserver_ctx *ctx, char *name, + char *port); + +/** + * @copydoc osd_hostmod_disconnect() + */ +osd_result osd_gdbserver_disconnect(struct osd_gdbserver_ctx *ctx); + +/** + * @copydoc osd_hostmod_is_connected() + */ +bool osd_gdbserver_is_connected(struct osd_gdbserver_ctx *ctx); + +/** + * Free the context object + */ +void osd_gdbserver_free(struct osd_gdbserver_ctx **ctx_p); + +/** + * Read data from the GDB client + * + * @return OSD_OK on success, any other value indicates an error + * + * @see osd_gdbserver_write_data() + */ +osd_result osd_gdbserver_read_data(struct osd_gdbserver_ctx *ctx); + +/** + * Write data to the GDB client + * + * @param data the data to be written 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 osd_gdbserver_read_data() + */ +osd_result osd_gdbserver_write_data(struct osd_gdbserver_ctx *ctx, char *data, + int len); + +/**@}*/ /* end of doxygen group libosd-gdbserver */ + +#ifdef __cplusplus +} +#endif + +#endif // OSD_GDBSERVER_H From acc9d98d83ba817afc8fcbe119602b175a6be3d2 Mon Sep 17 00:00:00 2001 From: Shivam Aggarwal Date: Fri, 6 Jul 2018 19:25:00 +0530 Subject: [PATCH 2/7] Added unit tests to test private functions These functions implement the RSP protocol and deal with the string buffer. --- src/libosd/gdbserver-private.h | 46 +++++++++++ src/libosd/gdbserver.c | 124 ++++++++++++++++------------- src/libosd/include/osd/gdbserver.h | 23 +++++- tests/unit/Makefile.am | 7 +- tests/unit/check_gdbserver.c | 75 +++++++++++++++++ 5 files changed, 215 insertions(+), 60 deletions(-) create mode 100644 src/libosd/gdbserver-private.h create mode 100644 tests/unit/check_gdbserver.c diff --git a/src/libosd/gdbserver-private.h b/src/libosd/gdbserver-private.h new file mode 100644 index 0000000..32b61a2 --- /dev/null +++ b/src/libosd/gdbserver-private.h @@ -0,0 +1,46 @@ +/* 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_PRIVATE_H +#define OSD_GDBSERVER_PRIVATE_H + +#include +#include + +#include + +/** + * Return packet-data from the received data buffer by validating the checksum + * + * @param buf_p the pointer to the received packet buffer data + * @param ver_checksum '1' indicates valid checksum + * @param len the length of the packet-data + * @param buffer the packet-data received + * + */ +osd_result validate_rsp_packet(char *buf_p, bool *ver_checksum, int *len, + char *buffer); + + +/** + * Set packet-data into the RSP format: $packet-data#checksum + * + * @param buffer the packet-data buffer + * @param len the length of the packet-data + * @param packet_buffer the packet buffer in RSP format + */ +osd_result configure_rsp_packet(char *buffer, int len, char *packet_buffer); + +#endif // OSD_GDBSERVER_PRIVATE_H diff --git a/src/libosd/gdbserver.c b/src/libosd/gdbserver.c index aa0a16f..7b352df 100644 --- a/src/libosd/gdbserver.c +++ b/src/libosd/gdbserver.c @@ -14,6 +14,7 @@ */ #include +#include "gdbserver-private.h" #include #include #include @@ -128,33 +129,6 @@ osd_result osd_gdbserver_connect(struct osd_gdbserver_ctx *ctx, char *name, return OSD_ERROR_CONNECTION_FAILED; } - struct sockaddr_in addr_in; - addr_in.sin_port = 0; - socklen_t addr_in_size = sizeof(addr_in); - - getsockname(ctx->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) { - ctx->client_fd = - accept(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); - if (OSD_FAILED(ctx->client_fd)) { - rv = close(ctx->client_fd); - if (OSD_SUCCEEDED(rv)) { - break; - } - } - printf("Server got connection from client %s\n", - inet_ntoa(addr_in.sin_addr)); - - if (OSD_SUCCEEDED(close(ctx->client_fd))) { - break; - } - } - - close(ctx->fd); - return OSD_OK; } @@ -185,6 +159,47 @@ void osd_gdbserver_free(struct osd_gdbserver_ctx **ctx_p) *ctx_p = NULL; } +API_EXPORT +osd_result osd_gdbserver_start(struct osd_gdbserver_ctx *ctx) +{ + osd_result rv; + + struct sockaddr_in addr_in; + addr_in.sin_port = 0; + socklen_t addr_in_size = sizeof(addr_in); + + getsockname(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); + printf("server started listening on port %d\n", ntohs(addr_in.sin_port)); + + while (1) { + ctx->client_fd = + accept(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); + if (OSD_FAILED(ctx->client_fd)) { + rv = close(ctx->client_fd); + if (OSD_SUCCEEDED(rv)) { + break; + } + } + + // At this point, connection is established between GDB client + // and gdbserver, and they are ready to transfer data. + // More functions to be added + printf("Server got connection from client %s\n", + inet_ntoa(addr_in.sin_addr)); + + if (OSD_SUCCEEDED(close(ctx->client_fd))) { + break; + } + } + return OSD_OK; +} + +API_EXPORT +osd_result osd_gdbserver_stop(struct osd_gdbserver_ctx *ctx) +{ + return close(ctx->fd); +} + API_EXPORT osd_result osd_gdbserver_read_data(struct osd_gdbserver_ctx *ctx) { @@ -225,8 +240,6 @@ osd_result osd_gdbserver_write_data(struct osd_gdbserver_ctx *ctx, char *data, static osd_result get_char(struct osd_gdbserver_ctx *ctx, int *ch) { - osd_result rv; - ctx->buf_p = ctx->buffer; ctx->buf_cnt--; if (OSD_FAILED(ctx->buf_cnt)) { @@ -237,30 +250,22 @@ static osd_result get_char(struct osd_gdbserver_ctx *ctx, int *ch) return OSD_OK; } -static osd_result validate_rsp_packet(struct osd_gdbserver_ctx *ctx, - bool *ver_checksum, int *len, - char *buffer) +//API_EXPORT +osd_result validate_rsp_packet(char *buf_p, 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 = ctx->buf_p; - int buf_cnt = ctx->buf_cnt; + char *buf = buf_p; // 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 @@ -269,7 +274,6 @@ static osd_result validate_rsp_packet(struct osd_gdbserver_ctx *ctx, if (packet_char == '}') { val_checksum += packet_char & 0xff; packet_char = *buf++; - i++; val_checksum += packet_char & 0xff; buffer[cnt++] = (packet_char ^ 0x20) & 0xff; } else { @@ -278,6 +282,7 @@ static osd_result validate_rsp_packet(struct osd_gdbserver_ctx *ctx, } } + buffer[cnt] = '\0'; *len = cnt; packet_char = *buf++; packet_checksum[0] = packet_char; @@ -303,7 +308,7 @@ static osd_result receive_rsp_packet(struct osd_gdbserver_ctx *ctx, } while (packet_char != '$'); bool ver_checksum = 0; - rv = validate_rsp_packet(ctx, &ver_checksum, len, buffer); + rv = validate_rsp_packet(ctx->buf_p, &ver_checksum, len, buffer); if (OSD_FAILED(rv)) { return rv; @@ -320,23 +325,32 @@ static osd_result receive_rsp_packet(struct osd_gdbserver_ctx *ctx, return OSD_OK; } +API_EXPORT +osd_result configure_rsp_packet(char *buffer, int len, char *packet_buffer) +{ + int packet_checksum = 0; + 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); + packet_buffer[j] = '\0'; + + return OSD_OK; +} + static osd_result send_rsp_packet(struct osd_gdbserver_ctx *ctx, char *buffer, int len) { - char packet_buffer[len + 3]; - int packet_checksum = 0; + char packet_buffer[len + 5]; 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); + configure_rsp_packet(buffer, len, packet_buffer); rv = osd_gdbserver_write_data(ctx, packet_buffer, len + 4); if (OSD_FAILED(rv)) { @@ -355,6 +369,4 @@ static osd_result send_rsp_packet(struct osd_gdbserver_ctx *ctx, char *buffer, return OSD_ERROR_FAILURE; } } - - return OSD_OK; } diff --git a/src/libosd/include/osd/gdbserver.h b/src/libosd/include/osd/gdbserver.h index 3ca6df0..3361960 100644 --- a/src/libosd/include/osd/gdbserver.h +++ b/src/libosd/include/osd/gdbserver.h @@ -16,9 +16,6 @@ #ifndef OSD_GDBSERVER_H #define OSD_GDBSERVER_H -#define OSD_GDBSERVER_PORT 5555 -#define OSD_GDBSERVER_BUFF_SIZE 1024 - #include #include @@ -37,6 +34,16 @@ extern "C" { struct osd_gdbserver_ctx; +/** + * Indicates the port for connecting to GDB + */ +#define OSD_GDBSERVER_PORT 5555 + +/** + * Indicates the size of the buffer + */ +#define OSD_GDBSERVER_BUFF_SIZE 1024 + /** * Create a new context object */ @@ -67,6 +74,16 @@ bool osd_gdbserver_is_connected(struct osd_gdbserver_ctx *ctx); */ void osd_gdbserver_free(struct osd_gdbserver_ctx **ctx_p); +/** + * Start the connection with GDB client + */ +osd_result osd_gdbserver_start(struct osd_gdbserver_ctx *ctx); + +/** + * Close the connection with GDB client + */ +osd_result osd_gdbserver_stop(struct osd_gdbserver_ctx *ctx); + /** * Read data from the GDB client * diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index ba0e7d2..d24695f 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -12,7 +12,8 @@ check_PROGRAMS = \ check_cl_cdm \ check_memaccess \ check_systracelogger \ - check_coretracelogger + check_coretracelogger \ + check_gdbserver check_hostmod_SOURCES = \ check_hostmod.c \ @@ -54,6 +55,10 @@ check_coretracelogger_SOURCES = \ check_coretracelogger.c \ mock_host_controller.c +check_gdbserver_SOURCE = \ + check_gdbserver.c \ + mock_host_controller.c + TESTS = $(check_PROGRAMS) AM_CFLAGS = \ diff --git a/tests/unit/check_gdbserver.c b/tests/unit/check_gdbserver.c new file mode 100644 index 0000000..59fb1e3 --- /dev/null +++ b/tests/unit/check_gdbserver.c @@ -0,0 +1,75 @@ +/* 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. + */ + +#define TEST_SUITE_NAME "check_gdbserver" + +#include "testutil.h" + +#include <../../src/libosd/gdbserver-private.h> +#include +#include + +#include "mock_host_controller.h" + +struct osd_gdbserver_ctx *gdbserver_ctx; +struct osd_log_ctx *log_ctx; + +const unsigned int target_subnet_addr = 0; +unsigned int mock_hostmod_diaddr; +unsigned int mock_scm_diaddr; + +START_TEST(test_validate_rsp_packet) +{ + osd_result rv; + char packet_buffer[12] = "swbreak#ef"; + char *buf_p = "swbreak#ef"; + bool ver_checksum; + int len; + char buffer[1024]; + + rv = validate_rsp_packet(buf_p, &ver_checksum, &len, buffer); + ck_assert(OSD_SUCCEEDED(rv)); + ck_assert_uint_eq(len, 7); + ck_assert_str_eq(buffer, "swbreak"); +} +END_TEST + +START_TEST(test_configure_rsp_packet) +{ + char buffer[8] = "swbreak"; + int packet_checksum; + int len = 7; + char packet_buffer[len + 5]; + + configure_rsp_packet(buffer, len, packet_buffer); + ck_assert_str_eq(packet_buffer, "$swbreak#ef"); +} +END_TEST + +Suite *suite(void) +{ + Suite *s; + TCase *tc_testing; + + s = suite_create(TEST_SUITE_NAME); + + tc_testing = tcase_create("Testing"); + + tcase_add_test(tc_testing, test_validate_rsp_packet); + tcase_add_test(tc_testing, test_configure_rsp_packet); + suite_add_tcase(s, tc_testing); + + return s; +} From b67a8c0ff7bde24150a3a6ecbb124d68c6415488 Mon Sep 17 00:00:00 2001 From: Shivam Aggarwal Date: Mon, 9 Jul 2018 22:51:23 +0530 Subject: [PATCH 3/7] Improves formatting and naming convention. Adds more test cases to check validate/encode functions. --- src/libosd/gdbserver-private.h | 31 +++++++------ src/libosd/gdbserver.c | 79 ++++++++++++++++++---------------- tests/unit/check_gdbserver.c | 62 ++++++++++++++++++-------- 3 files changed, 106 insertions(+), 66 deletions(-) diff --git a/src/libosd/gdbserver-private.h b/src/libosd/gdbserver-private.h index 32b61a2..157db3d 100644 --- a/src/libosd/gdbserver-private.h +++ b/src/libosd/gdbserver-private.h @@ -23,24 +23,31 @@ /** * Return packet-data from the received data buffer by validating the checksum + * + * It calculates the checksum of the obtained packet-data and compares it with + * the obtained checksum. This function ensures that the received packet-data + * is valid and uncorrupted. + * Refer https://sourceware.org/gdb/onlinedocs/gdb/Overview.html#Overview * - * @param buf_p the pointer to the received packet buffer data - * @param ver_checksum '1' indicates valid checksum - * @param len the length of the packet-data - * @param buffer the packet-data received + * @param packet_buffer the pointer to the received packet buffer data + * @param packet_len the length of th packet buffer + * @param packet_data_len the length of the packet-data + * @param packet_data the packet-data received * */ -osd_result validate_rsp_packet(char *buf_p, bool *ver_checksum, int *len, - char *buffer); - +bool validate_rsp_packet(char *packet_buffer, int packet_len, + int *packet_data_len, char *packet_data); /** * Set packet-data into the RSP format: $packet-data#checksum + * + * Refer https://sourceware.org/gdb/onlinedocs/gdb/Overview.html#Overview * - * @param buffer the packet-data buffer - * @param len the length of the packet-data - * @param packet_buffer the packet buffer in RSP format + * @param packet_data the packet-data buffer + * @param packet_data_len the length of the packet-data + * @param packet_buffer the packet buffer in RSP format */ -osd_result configure_rsp_packet(char *buffer, int len, char *packet_buffer); - +osd_result encode_rsp_packet(char *packet_data, int packet_data_len, + char *packet_buffer); + #endif // OSD_GDBSERVER_PRIVATE_H diff --git a/src/libosd/gdbserver.c b/src/libosd/gdbserver.c index 7b352df..7b8a1c0 100644 --- a/src/libosd/gdbserver.c +++ b/src/libosd/gdbserver.c @@ -14,10 +14,10 @@ */ #include -#include "gdbserver-private.h" #include #include #include +#include "gdbserver-private.h" #include "osd-private.h" #include @@ -250,19 +250,19 @@ static osd_result get_char(struct osd_gdbserver_ctx *ctx, int *ch) return OSD_OK; } -//API_EXPORT -osd_result validate_rsp_packet(char *buf_p, bool *ver_checksum, int *len, - char *buffer) +API_EXPORT +bool validate_rsp_packet(char *packet_buffer, int packet_len, + int *packet_data_len, char *packet_data) { unsigned char val_checksum = 0; char packet_checksum[3]; int packet_char; int cnt = 0; - char *buf = buf_p; + char *buf = packet_buffer; // packet-format: $packet-data#checksum // traversing through the obtained packet till we obtained '#' - while (1) { + while (packet_len >= 2) { packet_char = *buf++; if (packet_char == '#') { @@ -272,30 +272,33 @@ osd_result validate_rsp_packet(char *buf_p, bool *ver_checksum, int *len, * character followed by the original character XORed with 0x20. */ if (packet_char == '}') { - val_checksum += packet_char & 0xff; + val_checksum += packet_char; packet_char = *buf++; - val_checksum += packet_char & 0xff; - buffer[cnt++] = (packet_char ^ 0x20) & 0xff; + packet_len--; + val_checksum += packet_char; + packet_data[cnt++] = (packet_char ^ 0x20); } else { - val_checksum += packet_char & 0xff; - buffer[cnt++] = packet_char & 0xff; + val_checksum += packet_char; + packet_data[cnt++] = packet_char; } + packet_len--; } - buffer[cnt] = '\0'; - *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; + packet_data[cnt] = '\0'; + *packet_data_len = cnt; + bool ver_checksum = (val_checksum == strtoul(packet_checksum, NULL, 16)); + + return ver_checksum; } static osd_result receive_rsp_packet(struct osd_gdbserver_ctx *ctx, - char *buffer, int *len) + int *packet_data_len, char *packet_data) { int packet_char; osd_result rv; @@ -307,34 +310,36 @@ static osd_result receive_rsp_packet(struct osd_gdbserver_ctx *ctx, } } while (packet_char != '$'); - bool ver_checksum = 0; - rv = validate_rsp_packet(ctx->buf_p, &ver_checksum, len, buffer); + char *packet_buffer = ctx->buf_p; + int packet_len = ctx->buf_cnt; + bool ver_checksum = validate_rsp_packet(packet_buffer, packet_len, + packet_data_len, packet_data); + if (ver_checksum == 1) { + rv = osd_gdbserver_write_data(ctx, "+", 1); + } else { + rv = osd_gdbserver_write_data(ctx, "-", 1); + } if (OSD_FAILED(rv)) { return rv; - } else { - if (ver_checksum == 1) { - rv = osd_gdbserver_write_data(ctx, "+", 1); - } else { - rv = osd_gdbserver_write_data(ctx, "-", 1); - } - if (OSD_FAILED(rv)) { - return rv; - } } + return OSD_OK; } API_EXPORT -osd_result configure_rsp_packet(char *buffer, int len, char *packet_buffer) +osd_result encode_rsp_packet(char *packet_data, int packet_data_len, + char *packet_buffer) { int packet_checksum = 0; packet_buffer[0] = '$'; - memcpy(packet_buffer + 1, buffer, len); - int j = len + 1; + + memcpy(packet_buffer + 1, packet_data, packet_data_len); + int j = packet_data_len + 1; + packet_buffer[j++] = '#'; - for (int i = 0; i < len; i++) { - packet_checksum += buffer[i]; + for (int i = 0; i < packet_data_len; i++) { + packet_checksum += packet_data[i]; } packet_buffer[j++] = dectohex((packet_checksum >> 4) & 0xf); packet_buffer[j++] = dectohex(packet_checksum & 0xf); @@ -343,16 +348,16 @@ osd_result configure_rsp_packet(char *buffer, int len, char *packet_buffer) return OSD_OK; } -static osd_result send_rsp_packet(struct osd_gdbserver_ctx *ctx, char *buffer, - int len) +static osd_result send_rsp_packet(struct osd_gdbserver_ctx *ctx, + char *packet_data, int packet_data_len) { - char packet_buffer[len + 5]; + char packet_buffer[packet_data_len + 5]; osd_result rv; while (1) { - configure_rsp_packet(buffer, len, packet_buffer); + encode_rsp_packet(packet_data, packet_data_len, packet_buffer); - rv = osd_gdbserver_write_data(ctx, packet_buffer, len + 4); + rv = osd_gdbserver_write_data(ctx, packet_buffer, packet_data_len + 4); if (OSD_FAILED(rv)) { return OSD_ERROR_FAILURE; } diff --git a/tests/unit/check_gdbserver.c b/tests/unit/check_gdbserver.c index 59fb1e3..d1076e6 100644 --- a/tests/unit/check_gdbserver.c +++ b/tests/unit/check_gdbserver.c @@ -30,34 +30,60 @@ const unsigned int target_subnet_addr = 0; unsigned int mock_hostmod_diaddr; unsigned int mock_scm_diaddr; -START_TEST(test_validate_rsp_packet) +START_TEST(test1_validate_rsp_packet) { - osd_result rv; - char packet_buffer[12] = "swbreak#ef"; - char *buf_p = "swbreak#ef"; bool ver_checksum; - int len; - char buffer[1024]; + char *packet_buffer = "swbreak#ef"; + int packet_len = 10; + char packet_data[1024]; + int packet_data_len; + ver_checksum = validate_rsp_packet(packet_buffer, packet_len, + &packet_data_len, packet_data); + ck_assert_uint_eq(ver_checksum, 1); + ck_assert_uint_eq(packet_data_len, 7); + ck_assert_str_eq(packet_data, "swbreak"); +} +END_TEST - rv = validate_rsp_packet(buf_p, &ver_checksum, &len, buffer); - ck_assert(OSD_SUCCEEDED(rv)); - ck_assert_uint_eq(len, 7); - ck_assert_str_eq(buffer, "swbreak"); +START_TEST(test2_validate_rsp_packet) +{ + bool ver_checksum; + char *packet_buffer = "swbre}]ak#c9"; + int packet_len = 11; + char packet_data[1024]; + int packet_data_len; + ver_checksum = validate_rsp_packet(packet_buffer, packet_len, + &packet_data_len, packet_data); + ck_assert_uint_eq(ver_checksum, 1); + ck_assert_uint_eq(packet_data_len, 8); + ck_assert_str_eq(packet_data, "swbre}ak"); } END_TEST -START_TEST(test_configure_rsp_packet) +START_TEST(test1_encode_rsp_packet) { - char buffer[8] = "swbreak"; + char packet_data[8] = "swbreak"; int packet_checksum; - int len = 7; - char packet_buffer[len + 5]; + int packet_data_len = 7; + char packet_buffer[packet_data_len + 5]; - configure_rsp_packet(buffer, len, packet_buffer); + encode_rsp_packet(packet_data, packet_data_len, packet_buffer); ck_assert_str_eq(packet_buffer, "$swbreak#ef"); } END_TEST +START_TEST(test2_encode_rsp_packet) +{ + char packet_data[9] = "swbre:ak"; + int packet_checksum; + int packet_data_len = 8; + char packet_buffer[packet_data_len + 5]; + + encode_rsp_packet(packet_data, packet_data_len, packet_buffer); + ck_assert_str_eq(packet_buffer, "$swbre:ak#29"); +} +END_TEST + Suite *suite(void) { Suite *s; @@ -67,8 +93,10 @@ Suite *suite(void) tc_testing = tcase_create("Testing"); - tcase_add_test(tc_testing, test_validate_rsp_packet); - tcase_add_test(tc_testing, test_configure_rsp_packet); + tcase_add_test(tc_testing, test1_validate_rsp_packet); + tcase_add_test(tc_testing, test2_validate_rsp_packet); + tcase_add_test(tc_testing, test1_encode_rsp_packet); + tcase_add_test(tc_testing, test2_encode_rsp_packet); suite_add_tcase(s, tc_testing); return s; From 86cef0fd98be352f7f34443b54ada3fd23c6432c Mon Sep 17 00:00:00 2001 From: Shivam Aggarwal Date: Thu, 12 Jul 2018 19:34:59 +0530 Subject: [PATCH 4/7] Adds support for the following GDB commands: (1) 'g' : read general registers (2) 'G XX' : write general registers (3) 'p n ' : read the value of a specific register n (4) 'P n..=r..': write a specific register n with value r --- src/libosd/gdbserver.c | 157 ++++++++++++++++++++++++++++- src/libosd/include/osd/gdbserver.h | 3 +- 2 files changed, 157 insertions(+), 3 deletions(-) diff --git a/src/libosd/gdbserver.c b/src/libosd/gdbserver.c index 7b8a1c0..ce75591 100644 --- a/src/libosd/gdbserver.c +++ b/src/libosd/gdbserver.c @@ -13,6 +13,7 @@ * limitations under the License. */ +#include #include #include #include @@ -39,6 +40,8 @@ struct osd_gdbserver_ctx { struct osd_hostmod_ctx *hostmod_ctx; struct osd_log_ctx *log_ctx; + struct osd_cdm_desc cdm_desc; + uint16_t cdm_di_addr; int fd; char *name; char *port; @@ -53,7 +56,8 @@ struct osd_gdbserver_ctx { API_EXPORT osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, struct osd_log_ctx *log_ctx, - const char *host_controller_address) + const char *host_controller_address, + uint16_t cdm_di_addr) { osd_result rv; @@ -61,6 +65,7 @@ osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, assert(c); c->log_ctx = log_ctx; + c->cdm_di_addr = cdm_di_addr; struct osd_hostmod_ctx *hostmod_ctx; rv = osd_hostmod_new(&hostmod_ctx, log_ctx, host_controller_address, NULL, @@ -68,6 +73,8 @@ osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, assert(OSD_SUCCEEDED(rv)); c->hostmod_ctx = hostmod_ctx; + // c->cdm_desc = cdm_desc; + *ctx = c; return OSD_OK; @@ -298,7 +305,7 @@ bool validate_rsp_packet(char *packet_buffer, int packet_len, } static osd_result receive_rsp_packet(struct osd_gdbserver_ctx *ctx, - int *packet_data_len, char *packet_data) + char *packet_data, int *packet_data_len) { int packet_char; osd_result rv; @@ -375,3 +382,149 @@ static osd_result send_rsp_packet(struct osd_gdbserver_ctx *ctx, } } } + +static osd_result gdb_read_general_registers_cmd(struct osd_gdbserver_ctx *ctx, + char *packet_buffer, + int packet_len) +{ + osd_result rv; + // SPR register address mapped as GPR0 in OR1K + // GROUP 0 REG_NUM 1024 + uint16_t reg_addr = 0x400; + + rv = + osd_cl_cdm_get_desc(ctx->hostmod_ctx, ctx->cdm_di_addr, &ctx->cdm_desc); + if (OSD_FAILED(rv)) { + return rv; + } + + int core_reg_len = ctx->cdm_desc.core_data_width; + size_t reg_read_result[32]; + // + 1 for null character + // used for storing the read register value of all the GPRs + char *reg_packet = malloc((core_reg_len / 8) * 32 + 1); + + // read the value of each GPR from the CPU core and + // store it in the register packet + for (int i = 0; i < 32; i++) { + rv = cl_cdm_cpureg_read(ctx->hostmod_ctx, &ctx->cdm_desc, + ®_read_result[i], reg_addr + i, 0); + if (OSD_FAILED(rv)) { + return rv; + } + + char *reg_val = malloc((core_reg_len / 8) + 1); + sprintf(reg_val, "%02lx", reg_read_result[i]); + strcat(reg_packet, reg_val); + free(reg_val); + } + + rv = send_rsp_packet(ctx, reg_packet, (core_reg_len / 8) * 32 + 1); + if (OSD_FAILED(rv)) { + return rv; + } + + free(reg_packet); + + return OSD_OK; +} + +static osd_result gdb_write_general_registers_cmd(struct osd_gdbserver_ctx *ctx, + char *packet, int packet_len) +{ + osd_result rv; + rv = + osd_cl_cdm_get_desc(ctx->hostmod_ctx, ctx->cdm_di_addr, &ctx->cdm_desc); + if (OSD_FAILED(rv)) { + return rv; + } + + // increment by 1 to skip initial char 'G' + packet++; + // SPR register address mapped as GPR0 in OR1K + // GROUP 0 REG_NUM 1024 + uint16_t reg_addr = 0x400; + int core_reg_len = ctx->cdm_desc.core_data_width; + + // write the value of each GPR in the CPU core + for (int i = 0; i < 32; i++) { + char cur_reg_val[core_reg_len / 4 + 1]; + for (int j = 0; j < core_reg_len / 4; j++) { + cur_reg_val[j] = packet[i * core_reg_len + j]; + } + + cur_reg_val[core_reg_len / 4] = '\0'; + size_t reg_val = strtoul(cur_reg_val, NULL, 16); + rv = cl_cdm_cpureg_write(ctx->hostmod_ctx, &ctx->cdm_desc, ®_val, + reg_addr + i, 0); + if (OSD_FAILED(rv)) { + return rv; + } + } + + rv = send_rsp_packet(ctx, "OK", 2); + if (OSD_FAILED(rv)) { + return rv; + } + + return OSD_OK; +} + +static osd_result gdb_read_register_cmd(struct osd_gdbserver_ctx *ctx, + char *packet, int packet_len) +{ + osd_result rv; + uint16_t reg_addr = strtoul(packet + 1, NULL, 16); + + rv = + osd_cl_cdm_get_desc(ctx->hostmod_ctx, ctx->cdm_di_addr, &ctx->cdm_desc); + if (OSD_FAILED(rv)) { + return rv; + } + + int core_reg_len = ctx->cdm_desc.core_data_width; + size_t reg_read_result; + rv = cl_cdm_cpureg_read(ctx->hostmod_ctx, &ctx->cdm_desc, ®_read_result, + reg_addr, 0); + if (OSD_FAILED(rv)) { + return rv; + } + + char *reg_val = malloc(core_reg_len / 8 + 1); + sprintf(reg_val, "%02lx", reg_read_result); + rv = send_rsp_packet(ctx, reg_val, core_reg_len / 8 + 1); + if (OSD_FAILED(rv)) { + return rv; + } + + free(reg_val); + + return OSD_OK; +} + +static osd_result gdb_write_register_cmd(struct osd_gdbserver_ctx *ctx, + char *packet, int packet_len) +{ + osd_result rv; + char *equal_separator; + uint16_t reg_addr = strtoul(packet + 1, &equal_separator, 16); + rv = + osd_cl_cdm_get_desc(ctx->hostmod_ctx, ctx->cdm_di_addr, &ctx->cdm_desc); + if (OSD_FAILED(rv)) { + return rv; + } + + size_t reg_val = strtoul(equal_separator + 1, NULL, 16); + rv = cl_cdm_cpureg_write(ctx->hostmod_ctx, &ctx->cdm_desc, ®_val, + reg_addr, 0); + if (OSD_FAILED(rv)) { + return rv; + } + + rv = send_rsp_packet(ctx, "OK", 2); + if (OSD_FAILED(rv)) { + return rv; + } + + return OSD_OK; +} diff --git a/src/libosd/include/osd/gdbserver.h b/src/libosd/include/osd/gdbserver.h index 3361960..bcde3e0 100644 --- a/src/libosd/include/osd/gdbserver.h +++ b/src/libosd/include/osd/gdbserver.h @@ -49,7 +49,8 @@ struct osd_gdbserver_ctx; */ osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, struct osd_log_ctx *log_ctx, - const char *host_controller_address); + const char *host_controller_address, + uint16_t cdm_di_addr); /** * Connect GDB server to the host controller followed by GDB From 6fe0a2b1c22c0c7fcea0d572556012229cbb523d Mon Sep 17 00:00:00 2001 From: Shivam Aggarwal Date: Mon, 16 Jul 2018 11:22:20 +0530 Subject: [PATCH 5/7] Adds support for the following GDB commands/functions: 1. m addr,length : Read length bytes of memory starting at addr. 2. M addr,length:XX... : Write length bytes of memory starting at addr. 3. Unit tests to check intermediate utility functions --- src/libosd/gdbserver-private.h | 26 ++++- src/libosd/gdbserver.c | 165 +++++++++++++++++++++++++---- src/libosd/include/osd/gdbserver.h | 2 + tests/unit/check_gdbserver.c | 125 +++++++++++++++++++++- 4 files changed, 296 insertions(+), 22 deletions(-) diff --git a/src/libosd/gdbserver-private.h b/src/libosd/gdbserver-private.h index 157db3d..a7af2c4 100644 --- a/src/libosd/gdbserver-private.h +++ b/src/libosd/gdbserver-private.h @@ -23,7 +23,7 @@ /** * Return packet-data from the received data buffer by validating the checksum - * + * * It calculates the checksum of the obtained packet-data and compares it with * the obtained checksum. This function ensures that the received packet-data * is valid and uncorrupted. @@ -40,7 +40,7 @@ bool validate_rsp_packet(char *packet_buffer, int packet_len, /** * Set packet-data into the RSP format: $packet-data#checksum - * + * * Refer https://sourceware.org/gdb/onlinedocs/gdb/Overview.html#Overview * * @param packet_data the packet-data buffer @@ -50,4 +50,26 @@ bool validate_rsp_packet(char *packet_buffer, int packet_len, osd_result encode_rsp_packet(char *packet_data, int packet_data_len, char *packet_buffer); +/**Convert the read memory data into hexadecimal format + * + * Each byte of the memory is transmitted as a two-digit hexadecimal number. + * It is converted into the this format to transfer these values to the GDB. + * + * @param mem_val the read memory content + * @param mem_len the length of the memory content + * @param mem_hex the memory content in hexadecimal format + */ +void mem2hex(uint8_t *mem_val, size_t mem_len, uint8_t *mem_hex); + +/**Convert the hexadecimal data into unsigned int format to be placed in memory + * + * Each byte of the memory is transmitted as a two-digit hexadecimal number. + * It is converted into the unsigned int format to write the actual memory value + * + * @param mem_hex the memory content in hexadecimal format + * @param mem_len the length of the memory content + * @param mem_val the memory content to be placed + */ +void hex2mem(uint8_t *mem_hex, size_t mem_len, uint8_t *mem_val); + #endif // OSD_GDBSERVER_PRIVATE_H diff --git a/src/libosd/gdbserver.c b/src/libosd/gdbserver.c index ce75591..8280eaa 100644 --- a/src/libosd/gdbserver.c +++ b/src/libosd/gdbserver.c @@ -13,7 +13,6 @@ * limitations under the License. */ -#include #include #include #include @@ -41,7 +40,9 @@ struct osd_gdbserver_ctx { struct osd_hostmod_ctx *hostmod_ctx; struct osd_log_ctx *log_ctx; struct osd_cdm_desc cdm_desc; + struct osd_mem_desc mem_desc; uint16_t cdm_di_addr; + uint16_t mam_di_addr; int fd; char *name; char *port; @@ -73,20 +74,31 @@ osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, assert(OSD_SUCCEEDED(rv)); c->hostmod_ctx = hostmod_ctx; - // c->cdm_desc = cdm_desc; - *ctx = c; return OSD_OK; } -static int dectohex(int packet_char) +static char dectohex(int packet_char) { - if (packet_char < 10) { + if (packet_char >= 10 && packet_char <= 15) + return packet_char -10 + 'a'; + else if (packet_char >= 0 && packet_char <= 9) return packet_char + '0'; - } else { - return packet_char - 10 + 'a'; - } + else + return -1; +} + +static int hextodec(char packet_char) +{ + if (packet_char >= 'a' && packet_char <= 'f') + return packet_char + 10 - 'a'; + else if (packet_char >= 'A' && packet_char <= 'F') + return packet_char + 10 - 'A'; + else if (packet_char >= '0' && packet_char <= '9') + return packet_char - '0'; + else + return -1; } static void free_service(struct osd_gdbserver_ctx *ctx) @@ -188,9 +200,9 @@ osd_result osd_gdbserver_start(struct osd_gdbserver_ctx *ctx) } } - // At this point, connection is established between GDB client - // and gdbserver, and they are ready to transfer data. - // More functions to be added + /* At this point, connection is established between GDB client + and gdbserver, and they are ready to transfer data. + More functions to be added. */ printf("Server got connection from client %s\n", inet_ntoa(addr_in.sin_addr)); @@ -275,9 +287,9 @@ bool validate_rsp_packet(char *packet_buffer, int packet_len, if (packet_char == '#') { break; } - /*Any escaped byte (here, '}') is transmitted as the escape - * character followed by the original character XORed with 0x20. - */ + /* 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; packet_char = *buf++; @@ -388,8 +400,7 @@ static osd_result gdb_read_general_registers_cmd(struct osd_gdbserver_ctx *ctx, int packet_len) { osd_result rv; - // SPR register address mapped as GPR0 in OR1K - // GROUP 0 REG_NUM 1024 + // SPR register (GROUP 0 REG_NUM 1024) address mapped as GPR0 in OR1K uint16_t reg_addr = 0x400; rv = @@ -405,7 +416,7 @@ static osd_result gdb_read_general_registers_cmd(struct osd_gdbserver_ctx *ctx, char *reg_packet = malloc((core_reg_len / 8) * 32 + 1); // read the value of each GPR from the CPU core and - // store it in the register packet + // store it in the register packet. for (int i = 0; i < 32; i++) { rv = cl_cdm_cpureg_read(ctx->hostmod_ctx, &ctx->cdm_desc, ®_read_result[i], reg_addr + i, 0); @@ -441,8 +452,8 @@ static osd_result gdb_write_general_registers_cmd(struct osd_gdbserver_ctx *ctx, // increment by 1 to skip initial char 'G' packet++; - // SPR register address mapped as GPR0 in OR1K - // GROUP 0 REG_NUM 1024 + + // SPR register (GROUP 0 REG_NUM 1024) address mapped as GPR0 in OR1K uint16_t reg_addr = 0x400; int core_reg_len = ctx->cdm_desc.core_data_width; @@ -514,6 +525,10 @@ static osd_result gdb_write_register_cmd(struct osd_gdbserver_ctx *ctx, return rv; } + if (*equal_separator != '=') { + return OSD_ERROR_FAILURE; + } + size_t reg_val = strtoul(equal_separator + 1, NULL, 16); rv = cl_cdm_cpureg_write(ctx->hostmod_ctx, &ctx->cdm_desc, ®_val, reg_addr, 0); @@ -528,3 +543,115 @@ static osd_result gdb_write_register_cmd(struct osd_gdbserver_ctx *ctx, return OSD_OK; } + +API_EXPORT +void mem2hex(uint8_t *mem_val, size_t mem_len, uint8_t *mem_hex) +{ + size_t i; + for (i = 0; i < 2 * mem_len; i++) { + uint8_t temp = (mem_val[i / 2] >> (4 * ((i + 1) % 2))) & 0x0f; + mem_hex[i] = dectohex(temp); + } + mem_hex[i] = '\0'; +} + +API_EXPORT +void hex2mem(uint8_t *mem_hex, size_t mem_len, uint8_t *mem_val) +{ + size_t i; + uint8_t temp; + memset(mem_val, 0, mem_len); + for (i = 0; i < 2 * mem_len; i++) { + temp = hextodec(mem_hex[i]); + mem_val[i / 2] |= temp << (4 * ((i + 1) % 2)); + } + mem_val[mem_len] = 0; +} + +static osd_result gdb_read_memory_cmd(struct osd_gdbserver_ctx *ctx, + char *packet, int packet_len) +{ + osd_result rv; + uint64_t mem_addr; + size_t mem_len; + char *comma_separator; + + rv = osd_cl_mam_get_mem_desc(ctx->hostmod_ctx, ctx->mam_di_addr, + &ctx->mem_desc); + if (OSD_FAILED(rv)) { + return rv; + } + + packet++; + mem_addr = strtoul(packet, &comma_separator, 16); + if (*comma_separator != ',') { + return OSD_ERROR_FAILURE; + } + + mem_len = strtoul(comma_separator + 1, NULL, 16); + if (!mem_len) { + return OSD_ERROR_FAILURE; + } + + size_t mem_read_result; + rv = osd_cl_mam_read(&ctx->mem_desc, ctx->hostmod_ctx, &mem_read_result, + mem_len, mem_addr); + + // Each byte is represented as two hex characters + uint8_t *mem_packet_content = malloc(mem_len * 2 + 1); + uint8_t *mem_val = (uint8_t *)&mem_read_result; + mem2hex(mem_val, mem_len, mem_packet_content); + + rv = send_rsp_packet(ctx, (char *)mem_packet_content, mem_len * 2 + 1); + if (OSD_FAILED(rv)) { + return rv; + } + + free(mem_packet_content); +} + +static osd_result gdb_write_memory_cmd(struct osd_gdbserver_ctx *ctx, + char *packet, int packet_len) +{ + osd_result rv; + uint64_t mem_addr; + size_t mem_len; + char *comma_separator; + char *colon_separator; + + rv = osd_cl_mam_get_mem_desc(ctx->hostmod_ctx, ctx->mam_di_addr, + &ctx->mem_desc); + if (OSD_FAILED(rv)) { + return rv; + } + + packet++; + mem_addr = strtoul(packet, &comma_separator, 16); + if (*comma_separator != ',') { + return OSD_ERROR_FAILURE; + } + + mem_len = strtoul(comma_separator + 1, &colon_separator, 16); + if (!mem_len) { + return OSD_ERROR_FAILURE; + } + + if (*colon_separator != ':') { + return OSD_ERROR_FAILURE; + } + + uint8_t *mem_packet_content = (uint8_t *)colon_separator + 1; + // Each byte is represented as two hex characters + uint8_t *mem_write_result = malloc(mem_len); + hex2mem(mem_packet_content, mem_len, mem_write_result); + + rv = osd_cl_mam_write(&ctx->mem_desc, ctx->hostmod_ctx, mem_write_result, + mem_len, mem_addr); + + free(mem_write_result); + + rv = send_rsp_packet(ctx, "OK", 2); + if (OSD_FAILED(rv)) { + return rv; + } +} diff --git a/src/libosd/include/osd/gdbserver.h b/src/libosd/include/osd/gdbserver.h index bcde3e0..ce4bc9a 100644 --- a/src/libosd/include/osd/gdbserver.h +++ b/src/libosd/include/osd/gdbserver.h @@ -18,6 +18,8 @@ #include #include +#include +#include #include diff --git a/tests/unit/check_gdbserver.c b/tests/unit/check_gdbserver.c index d1076e6..7772ce1 100644 --- a/tests/unit/check_gdbserver.c +++ b/tests/unit/check_gdbserver.c @@ -37,8 +37,12 @@ START_TEST(test1_validate_rsp_packet) int packet_len = 10; char packet_data[1024]; int packet_data_len; + + // checks if the obtained packet-data and checksum are valid ver_checksum = validate_rsp_packet(packet_buffer, packet_len, &packet_data_len, packet_data); + + // ver_checksum = 1 indicates valid packet-data ck_assert_uint_eq(ver_checksum, 1); ck_assert_uint_eq(packet_data_len, 7); ck_assert_str_eq(packet_data, "swbreak"); @@ -49,17 +53,58 @@ START_TEST(test2_validate_rsp_packet) { bool ver_checksum; char *packet_buffer = "swbre}]ak#c9"; - int packet_len = 11; + int packet_len = 12; char packet_data[1024]; int packet_data_len; + + // checks if the obtained packet-data and checksum are valid ver_checksum = validate_rsp_packet(packet_buffer, packet_len, &packet_data_len, packet_data); + + // ver_checksum = 1 indicates valid packet-data ck_assert_uint_eq(ver_checksum, 1); ck_assert_uint_eq(packet_data_len, 8); ck_assert_str_eq(packet_data, "swbre}ak"); } END_TEST +START_TEST(test3_validate_rsp_packet) +{ + bool ver_checksum; + char *packet_buffer = "M23,4:ef0352ab#a4"; + int packet_len = 17; + char packet_data[1024]; + int packet_data_len; + + // checks if the obtained packet-data and checksum are valid + ver_checksum = validate_rsp_packet(packet_buffer, packet_len, + &packet_data_len, packet_data); + + // ver_checksum = 1 indicates valid packet-data + ck_assert_uint_eq(ver_checksum, 1); + ck_assert_uint_eq(packet_data_len, 14); + ck_assert_str_eq(packet_data, "M23,4:ef0352ab"); +} +END_TEST + +START_TEST(test4_validate_rsp_packet) +{ + bool ver_checksum; + char *packet_buffer = "m23,4#a4"; + int packet_len = 8; + char packet_data[1024]; + int packet_data_len; + + // checks if the obtained packet-data and checksum are valid + ver_checksum = validate_rsp_packet(packet_buffer, packet_len, + &packet_data_len, packet_data); + + // ver_checksum = 1 indicates valid packet-data + // Here, the obtained checksum is not correct + ck_assert_uint_eq(ver_checksum, 0); +} +END_TEST + START_TEST(test1_encode_rsp_packet) { char packet_data[8] = "swbreak"; @@ -67,6 +112,7 @@ START_TEST(test1_encode_rsp_packet) int packet_data_len = 7; char packet_buffer[packet_data_len + 5]; + // encodes the packet-data into RSP packet format: $packet-data#checksum encode_rsp_packet(packet_data, packet_data_len, packet_buffer); ck_assert_str_eq(packet_buffer, "$swbreak#ef"); } @@ -79,11 +125,82 @@ START_TEST(test2_encode_rsp_packet) int packet_data_len = 8; char packet_buffer[packet_data_len + 5]; + // encodes the packet-data into RSP packet format: $packet-data#checksum encode_rsp_packet(packet_data, packet_data_len, packet_buffer); ck_assert_str_eq(packet_buffer, "$swbre:ak#29"); } END_TEST +START_TEST(test1_mem_to_hex) +{ + // 2 bytes of data read from the memory + size_t mem_read_result = 0xaf03; + uint8_t *mem_content = (uint8_t *)&mem_read_result; + size_t mem_len = 2; + uint8_t *mem_content_val = malloc(2 * mem_len + 1); + + // conversion into hexadecimal format + mem2hex(mem_content, mem_len, mem_content_val); + + ck_assert_str_eq(mem_content_val, "03af"); + + free(mem_content_val); +} +END_TEST + +START_TEST(test2_mem_to_hex) +{ + // 3 bytes of data read from the memory + size_t mem_read_result = 0x45e03f; + uint8_t *mem_content = (uint8_t *)&mem_read_result; + size_t mem_len = 3; + uint8_t *mem_content_val = malloc(2 * mem_len + 1); + + // conversion into hexadecimal format + mem2hex(mem_content, mem_len, mem_content_val); + + ck_assert_str_eq(mem_content_val, "3fe045"); + + free(mem_content_val); +} +END_TEST + +START_TEST(test1_hex_to_mem) +{ + // 5 bytes of data to be written to the memory + uint8_t *mem_content = "9f4a4034ef"; + size_t mem_len = 5; + uint8_t *mem_write_result = malloc(mem_len + 1); + + // conversion from hexadecimal format + hex2mem(mem_content, mem_len, mem_write_result); + + ck_assert_uint_eq(mem_write_result[0], 159); + ck_assert_uint_eq(mem_write_result[1], 74); + ck_assert_uint_eq(mem_write_result[2], 64); + ck_assert_uint_eq(mem_write_result[3], 52); + ck_assert_uint_eq(mem_write_result[4], 239); + + free(mem_write_result); +} +END_TEST + +START_TEST(test2_hex_to_mem) +{ + // 1 byte of data to be written to the memory + uint8_t *mem_content = "ef"; + size_t mem_len = 1; + uint8_t *mem_write_result = malloc(mem_len + 1); + + // conversion from hexadecimal format + hex2mem(mem_content, mem_len, mem_write_result); + + ck_assert_uint_eq(mem_write_result[0], 239); + + free(mem_write_result); +} +END_TEST + Suite *suite(void) { Suite *s; @@ -95,8 +212,14 @@ Suite *suite(void) tcase_add_test(tc_testing, test1_validate_rsp_packet); tcase_add_test(tc_testing, test2_validate_rsp_packet); + tcase_add_test(tc_testing, test3_validate_rsp_packet); + tcase_add_test(tc_testing, test4_validate_rsp_packet); tcase_add_test(tc_testing, test1_encode_rsp_packet); tcase_add_test(tc_testing, test2_encode_rsp_packet); + tcase_add_test(tc_testing, test1_mem_to_hex); + tcase_add_test(tc_testing, test2_mem_to_hex); + tcase_add_test(tc_testing, test1_hex_to_mem); + tcase_add_test(tc_testing, test2_hex_to_mem); suite_add_tcase(s, tc_testing); return s; From ccb85ca3fef10f3b7a6286b8dd2ac2a983689a47 Mon Sep 17 00:00:00 2001 From: Shivam Aggarwal Date: Sun, 22 Jul 2018 18:11:15 +0530 Subject: [PATCH 6/7] Adds mock_GDBclient program to test various functionality of GDB. --- src/libosd/gdbserver.c | 144 +++++++++++++++++++++-------- src/libosd/include/osd/gdbserver.h | 27 +++--- tests/unit/Makefile.am | 3 +- tests/unit/check_gdbserver.c | 98 +++++++++++++++++++- tests/unit/mock_gdbclient.c | 114 +++++++++++++++++++++++ tests/unit/mock_gdbclient.h | 41 ++++++++ 6 files changed, 370 insertions(+), 57 deletions(-) create mode 100644 tests/unit/mock_gdbclient.c create mode 100644 tests/unit/mock_gdbclient.h diff --git a/src/libosd/gdbserver.c b/src/libosd/gdbserver.c index 8280eaa..56c4f3b 100644 --- a/src/libosd/gdbserver.c +++ b/src/libosd/gdbserver.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,8 @@ #include #include +pthread_t osd_gdbserver_thread; + /** * OSD-GDB server context */ @@ -44,8 +47,6 @@ struct osd_gdbserver_ctx { uint16_t cdm_di_addr; uint16_t mam_di_addr; int fd; - char *name; - char *port; struct sockaddr_in sin; char buffer[OSD_GDBSERVER_BUFF_SIZE]; int buf_cnt; @@ -58,7 +59,7 @@ API_EXPORT osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, struct osd_log_ctx *log_ctx, const char *host_controller_address, - uint16_t cdm_di_addr) + uint16_t cdm_di_addr, uint16_t mam_di_addr) { osd_result rv; @@ -67,7 +68,7 @@ osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, c->log_ctx = log_ctx; c->cdm_di_addr = cdm_di_addr; - + c->mam_di_addr = mam_di_addr; struct osd_hostmod_ctx *hostmod_ctx; rv = osd_hostmod_new(&hostmod_ctx, log_ctx, host_controller_address, NULL, NULL); @@ -82,7 +83,7 @@ osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, static char dectohex(int packet_char) { if (packet_char >= 10 && packet_char <= 15) - return packet_char -10 + 'a'; + return packet_char - 10 + 'a'; else if (packet_char >= 0 && packet_char <= 9) return packet_char + '0'; else @@ -101,30 +102,26 @@ static int hextodec(char packet_char) return -1; } -static void free_service(struct osd_gdbserver_ctx *ctx) -{ - free(ctx->name); - free(ctx->port); - free(ctx); -} - API_EXPORT -osd_result osd_gdbserver_connect(struct osd_gdbserver_ctx *ctx, char *name, - char *port) +osd_result osd_gdbserver_connect_hostmod(struct osd_gdbserver_ctx *ctx) { osd_result rv = osd_hostmod_connect(ctx->hostmod_ctx); if (OSD_FAILED(rv)) { return rv; } + return OSD_OK; +} + +API_EXPORT +osd_result osd_gdbserver_connect_GDB(struct osd_gdbserver_ctx *ctx) +{ int sockoptval = 1; - ctx->name = strdup(name); - ctx->port = strdup(port); ctx->fd = socket(AF_INET, SOCK_STREAM, 0); if (OSD_FAILED(ctx->fd)) { - free_service(ctx); + osd_gdbserver_free(&ctx); return OSD_ERROR_CONNECTION_FAILED; } @@ -138,13 +135,16 @@ osd_result osd_gdbserver_connect(struct osd_gdbserver_ctx *ctx, char *name, if (OSD_FAILED( bind(ctx->fd, (struct sockaddr *)&ctx->sin, sizeof(ctx->sin)))) { close(ctx->fd); - free_service(ctx); + osd_gdbserver_free(&ctx); return OSD_ERROR_CONNECTION_FAILED; } + /* Change the socket into non-blocking state */ + fcntl(ctx->fd, F_SETFL, O_NONBLOCK); + if (OSD_FAILED(listen(ctx->fd, 1))) { close(ctx->fd); - free_service(ctx); + osd_gdbserver_free(&ctx); return OSD_ERROR_CONNECTION_FAILED; } @@ -152,13 +152,13 @@ osd_result osd_gdbserver_connect(struct osd_gdbserver_ctx *ctx, char *name, } API_EXPORT -osd_result osd_gdbserver_disconnect(struct osd_gdbserver_ctx *ctx) +osd_result osd_gdbserver_disconnect_hostmod(struct osd_gdbserver_ctx *ctx) { return osd_hostmod_disconnect(ctx->hostmod_ctx); } API_EXPORT -bool osd_gdbserver_is_connected(struct osd_gdbserver_ctx *ctx) +bool osd_gdbserver_is_connected_hostmod(struct osd_gdbserver_ctx *ctx) { return osd_hostmod_is_connected(ctx->hostmod_ctx); } @@ -178,9 +178,9 @@ void osd_gdbserver_free(struct osd_gdbserver_ctx **ctx_p) *ctx_p = NULL; } -API_EXPORT -osd_result osd_gdbserver_start(struct osd_gdbserver_ctx *ctx) +static osd_result handle_rsp_packet(void *arg) { + struct osd_gdbserver_ctx *ctx = (struct osd_gdbserver_ctx *)arg; osd_result rv; struct sockaddr_in addr_in; @@ -190,33 +190,94 @@ osd_result osd_gdbserver_start(struct osd_gdbserver_ctx *ctx) getsockname(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); printf("server started listening on port %d\n", ntohs(addr_in.sin_port)); - while (1) { - ctx->client_fd = - accept(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); - if (OSD_FAILED(ctx->client_fd)) { - rv = close(ctx->client_fd); - if (OSD_SUCCEEDED(rv)) { - break; - } - } + ctx->client_fd = + accept(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); + + if (OSD_FAILED(ctx->client_fd)) { + close(ctx->client_fd); + return OSD_ERROR_FAILURE; + } - /* At this point, connection is established between GDB client - and gdbserver, and they are ready to transfer data. - More functions to be added. */ - printf("Server got connection from client %s\n", - inet_ntoa(addr_in.sin_addr)); + printf("Server got connection from client %s\n", + inet_ntoa(addr_in.sin_addr)); - if (OSD_SUCCEEDED(close(ctx->client_fd))) { - break; + /* Change the socket into non-blocking state */ + fcntl(ctx->client_fd, F_SETFL, O_NONBLOCK); + + int packet_len; + char rsp_packet_buffer[OSD_GDBSERVER_BUFF_SIZE]; + + osd_gdbserver_read_data(ctx); + + // osd_gdbserver_write_data(ctx, "+", 1); + // // rv = osd_gdbserver_read_data(ctx); + // if (OSD_FAILED(rv)) { + // return rv; + // } + + /* + rv = receive_rsp_packet(ctx, rsp_packet_buffer, &packet_len); + if (OSD_FAILED(rv)) { + return rv; + } + + if (packet_len > 0) { + switch (rsp_packet_buffer[0]) { + case '?': + break; + case 'M': + break; + case 'm': + break; + case 'g': + rv = get_registers_packet(ctx, rsp_packet_buffer, packet_len); + break; + case 'G': + break; + case 'p': + rv = get_register_packet(ctx, rsp_packet_buffer, packet_len); + break; + case 'P': + rv = set_register_packet(ctx, rsp_packet_buffer, packet_len); + case 'z': + case 'Z': + case 'c': + case 's': + default: + // Unsupported or unknown packet + osd_gdbserver_write_data(ctx, NULL, 0); + break; } } + */ return OSD_OK; } +static void *gdbserver_thread_routine(void *arg) +{ + handle_rsp_packet(arg); + return NULL; +} + API_EXPORT -osd_result osd_gdbserver_stop(struct osd_gdbserver_ctx *ctx) +osd_result osd_gdbserver_start(struct osd_gdbserver_ctx *ctx) +{ + osd_result rv; + + osd_gdbserver_connect_GDB(ctx); + rv = pthread_create(&osd_gdbserver_thread, 0, gdbserver_thread_routine, + (void *)ctx); + if (OSD_FAILED(rv)) { + return rv; + } + + return OSD_OK; +} + +void osd_gdbserver_stop(struct osd_gdbserver_ctx *ctx) { - return close(ctx->fd); + close(ctx->fd); + pthread_join(osd_gdbserver_thread, NULL); } API_EXPORT @@ -249,6 +310,7 @@ osd_result osd_gdbserver_write_data(struct osd_gdbserver_ctx *ctx, char *data, if (ctx->closed == 1) { return OSD_ERROR_NOT_CONNECTED; } + printf("Server:Data send- %s", data); int wlen = write(ctx->client_fd, data, len); if (wlen == len) { return OSD_OK; diff --git a/src/libosd/include/osd/gdbserver.h b/src/libosd/include/osd/gdbserver.h index ce4bc9a..d6b9233 100644 --- a/src/libosd/include/osd/gdbserver.h +++ b/src/libosd/include/osd/gdbserver.h @@ -16,10 +16,10 @@ #ifndef OSD_GDBSERVER_H #define OSD_GDBSERVER_H -#include -#include #include #include +#include +#include #include @@ -52,25 +52,31 @@ struct osd_gdbserver_ctx; osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, struct osd_log_ctx *log_ctx, const char *host_controller_address, - uint16_t cdm_di_addr); + uint16_t cdm_di_addr, uint16_t mam_di_addr); + +/** + * Connect GDB server to the GDB client + * + * @return OSD_OK on success, any other value indicates an error + */ +osd_result osd_gdbserver_connect_GDB(struct osd_gdbserver_ctx *ctx); /** - * Connect GDB server to the host controller followed by GDB + * Connect GDB server to the host controller * * @return OSD_OK on success, any other value indicates an error */ -osd_result osd_gdbserver_connect(struct osd_gdbserver_ctx *ctx, char *name, - char *port); +osd_result osd_gdbserver_connect_hostmod(struct osd_gdbserver_ctx *ctx); /** * @copydoc osd_hostmod_disconnect() */ -osd_result osd_gdbserver_disconnect(struct osd_gdbserver_ctx *ctx); +osd_result osd_gdbserver_disconnect_hostmod(struct osd_gdbserver_ctx *ctx); /** * @copydoc osd_hostmod_is_connected() */ -bool osd_gdbserver_is_connected(struct osd_gdbserver_ctx *ctx); +bool osd_gdbserver_is_connected_hostmod(struct osd_gdbserver_ctx *ctx); /** * Free the context object @@ -83,10 +89,9 @@ void osd_gdbserver_free(struct osd_gdbserver_ctx **ctx_p); osd_result osd_gdbserver_start(struct osd_gdbserver_ctx *ctx); /** - * Close the connection with GDB client + * Stop the connection with GDB client */ -osd_result osd_gdbserver_stop(struct osd_gdbserver_ctx *ctx); - +void osd_gdbserver_stop(struct osd_gdbserver_ctx *ctx); /** * Read data from the GDB client * diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index d24695f..98a2ca8 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -55,8 +55,9 @@ check_coretracelogger_SOURCES = \ check_coretracelogger.c \ mock_host_controller.c -check_gdbserver_SOURCE = \ +check_gdbserver_SOURCES = \ check_gdbserver.c \ + mock_gdbclient.c \ mock_host_controller.c TESTS = $(check_PROGRAMS) diff --git a/tests/unit/check_gdbserver.c b/tests/unit/check_gdbserver.c index 7772ce1..82bd4f0 100644 --- a/tests/unit/check_gdbserver.c +++ b/tests/unit/check_gdbserver.c @@ -15,20 +15,106 @@ #define TEST_SUITE_NAME "check_gdbserver" +#include "mock_gdbclient.h" #include "testutil.h" -#include <../../src/libosd/gdbserver-private.h> #include #include +#include <../../src/libosd/gdbserver-private.h> +#include + +#include #include "mock_host_controller.h" struct osd_gdbserver_ctx *gdbserver_ctx; struct osd_log_ctx *log_ctx; +struct mock_gdbclient_ctx *gdbclient_ctx; const unsigned int target_subnet_addr = 0; unsigned int mock_hostmod_diaddr; -unsigned int mock_scm_diaddr; +unsigned int mock_cdm_diaddr; +unsigned int mock_mam_diaddr; + +pthread_t mock_gdbserver_thread; +volatile int mock_gdbserver_ready; +volatile int mock_server_thread_cancel; + +void setup_hostmod(void) +{ + osd_result rv; + + log_ctx = testutil_get_log_ctx(); + + // initialize module context + rv = osd_gdbserver_new(&gdbserver_ctx, log_ctx, "inproc://testing", + mock_cdm_diaddr, mock_mam_diaddr); + + ck_assert_int_eq(rv, OSD_OK); + ck_assert_ptr_ne(gdbserver_ctx, NULL); + + rv = mock_gdbclient_new(&gdbclient_ctx); + ck_assert_int_eq(rv, OSD_OK); + + // connect + mock_host_controller_expect_diaddr_req(mock_hostmod_diaddr); + + rv = osd_gdbserver_connect_hostmod(gdbserver_ctx); + ck_assert_int_eq(rv, OSD_OK); +} + +void teardown_hostmod(void) +{ + osd_result rv; + + rv = osd_gdbserver_disconnect_hostmod(gdbserver_ctx); + ck_assert_int_eq(rv, OSD_OK); +} + +/** + * Test fixture: setup (called before each tests) + */ +void setup(void) +{ + osd_result rv; + + mock_hostmod_diaddr = osd_diaddr_build(1, 1); + mock_cdm_diaddr = osd_diaddr_build(target_subnet_addr, 5); + mock_mam_diaddr = osd_diaddr_build(target_subnet_addr, 10); + mock_host_controller_setup(); + setup_hostmod(); + + rv = osd_gdbserver_start(gdbserver_ctx); + ck_assert_int_eq(rv, OSD_OK); + + rv = mock_gdbclient_connect(gdbclient_ctx); + ck_assert_int_eq(rv, OSD_OK); +} + +/** + * Test fixture: teardown (called after each test) + */ +void teardown(void) +{ + mock_host_controller_wait_for_event_tx(); + teardown_hostmod(); + mock_host_controller_teardown(); + + osd_gdbserver_stop(gdbserver_ctx); + + osd_gdbserver_free(&gdbserver_ctx); + ck_assert_ptr_eq(gdbserver_ctx, NULL); + + mock_gdbclient_free(&gdbclient_ctx); + ck_assert_ptr_eq(gdbclient_ctx, NULL); +} + +START_TEST(test_init_base) +{ + setup(); + teardown(); +} +END_TEST START_TEST(test1_validate_rsp_packet) { @@ -204,12 +290,16 @@ END_TEST Suite *suite(void) { Suite *s; - TCase *tc_testing; + TCase *tc_testing, *tc_init; s = suite_create(TEST_SUITE_NAME); - tc_testing = tcase_create("Testing"); + tc_init = tcase_create("Init"); + tcase_add_test(tc_init, test_init_base); + tcase_set_timeout(tc_init, 120); + suite_add_tcase(s, tc_init); + tc_testing = tcase_create("Testing"); tcase_add_test(tc_testing, test1_validate_rsp_packet); tcase_add_test(tc_testing, test2_validate_rsp_packet); tcase_add_test(tc_testing, test3_validate_rsp_packet); diff --git a/tests/unit/mock_gdbclient.c b/tests/unit/mock_gdbclient.c new file mode 100644 index 0000000..2046838 --- /dev/null +++ b/tests/unit/mock_gdbclient.c @@ -0,0 +1,114 @@ +/* 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 "mock_gdbclient.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +osd_result mock_gdbclient_new(struct mock_gdbclient_ctx **ctx) +{ + struct mock_gdbclient_ctx *c = calloc(1, sizeof(struct mock_gdbclient_ctx)); + assert(c); + + c->fd = 0; + c->closed = 0; + c->buf_cnt = 0; + *ctx = c; + + return OSD_OK; +} + +osd_result mock_gdbclient_connect(struct mock_gdbclient_ctx *ctx) +{ + osd_result rv; + + struct sockaddr_in gdb_serv_addr; + + if (OSD_FAILED(ctx->fd = socket(AF_INET, SOCK_STREAM, 0))) { + return OSD_ERROR_CONNECTION_FAILED; + } + + memset(&gdb_serv_addr, '0', sizeof(gdb_serv_addr)); + + gdb_serv_addr.sin_family = AF_INET; + gdb_serv_addr.sin_port = htons(MOCK_GDBCLIENT_PORT); + + if (OSD_FAILED(inet_pton(AF_INET, "127.0.0.1", &gdb_serv_addr.sin_addr))) { + return OSD_ERROR_CONNECTION_FAILED; + } + + if (OSD_FAILED(connect(ctx->fd, (struct sockaddr *)&gdb_serv_addr, + sizeof(gdb_serv_addr)))) { + return OSD_ERROR_CONNECTION_FAILED; + } + + mock_gdbclient_write_data(ctx, "OK", 2); + + return OSD_OK; +} + +osd_result mock_gdbclient_read_data(struct mock_gdbclient_ctx *ctx) +{ + // memset(ctx->buffer, 0, sizeof ctx->buffer); + ctx->buf_cnt = read(ctx->fd, ctx->buffer, MOCK_GDBCLIENT_BUFF_SIZE); + + if (OSD_FAILED(ctx->buf_cnt)) { + return OSD_ERROR_CONNECTION_FAILED; + } else { + if (ctx->buf_cnt > 0) { + printf("Client:Packet Received %s\n", ctx->buffer); + printf("Client of Packet:%d\n", ctx->buf_cnt); + return OSD_OK; + } + if (ctx->buf_cnt == 0) { + ctx->closed = 1; + return OSD_ERROR_FAILURE; + } + } + return OSD_OK; +} + +osd_result mock_gdbclient_write_data(struct mock_gdbclient_ctx *ctx, char *data, + int len) +{ + if (ctx->closed == 1) { + return OSD_ERROR_NOT_CONNECTED; + } + int wlen = write(ctx->fd, data, len); + if (wlen == len) { + return OSD_OK; + } + + return OSD_ERROR_NOT_CONNECTED; +} + +void mock_gdbclient_free(struct mock_gdbclient_ctx **ctx_p) +{ + assert(ctx_p); + struct mock_gdbclient_ctx *ctx = *ctx_p; + if (!ctx) { + return; + } + + free(ctx); + *ctx_p = NULL; +} diff --git a/tests/unit/mock_gdbclient.h b/tests/unit/mock_gdbclient.h new file mode 100644 index 0000000..75294a9 --- /dev/null +++ b/tests/unit/mock_gdbclient.h @@ -0,0 +1,41 @@ +/* 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 MOCK_GDBCLIENT_H +#define MOCK_GDBCLIENT_H + +#include + +#include +#include + +#define MOCK_GDBCLIENT_PORT 5555 +#define MOCK_GDBCLIENT_BUFF_SIZE 1024 + +struct mock_gdbclient_ctx { + int fd; + int closed; + int buf_cnt; + char buffer[MOCK_GDBCLIENT_BUFF_SIZE]; +}; + +osd_result mock_gdbclient_new(struct mock_gdbclient_ctx **ctx); +osd_result mock_gdbclient_connect(struct mock_gdbclient_ctx *ctx); +osd_result mock_gdbclient_write_data(struct mock_gdbclient_ctx *ctx, char *data, + int len); +osd_result mock_gdbclient_read_data(struct mock_gdbclient_ctx *ctx); +void mock_gdbclient_free(struct mock_gdbclient_ctx **ctx_p); + +#endif // MOCK_GDBCLIENT_H From 9d423306d5dd9e0fb71e5fbcaa46ed835fc612d4 Mon Sep 17 00:00:00 2001 From: Shivam Aggarwal Date: Sun, 5 Aug 2018 15:32:34 +0530 Subject: [PATCH 7/7] Resolve major issues with coding standards and documentation --- src/libosd/gdbserver.c | 456 ++++++++++++++++------------- src/libosd/include/osd/gdbserver.h | 72 ++--- tests/unit/check_gdbserver.c | 34 ++- tests/unit/mock_gdbclient.c | 67 ++++- tests/unit/mock_gdbclient.h | 8 +- 5 files changed, 371 insertions(+), 266 deletions(-) diff --git a/src/libosd/gdbserver.c b/src/libosd/gdbserver.c index 56c4f3b..4fe98e6 100644 --- a/src/libosd/gdbserver.c +++ b/src/libosd/gdbserver.c @@ -13,6 +13,8 @@ * limitations under the License. */ +#include +#include #include #include #include @@ -47,12 +49,14 @@ struct osd_gdbserver_ctx { uint16_t cdm_di_addr; uint16_t mam_di_addr; int fd; - struct sockaddr_in sin; + int port; + int addr; + int client_fd; char buffer[OSD_GDBSERVER_BUFF_SIZE]; int buf_cnt; char *buf_p; int closed; - int client_fd; + osd_result rv; }; API_EXPORT @@ -69,6 +73,7 @@ osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, c->log_ctx = log_ctx; c->cdm_di_addr = cdm_di_addr; c->mam_di_addr = mam_di_addr; + struct osd_hostmod_ctx *hostmod_ctx; rv = osd_hostmod_new(&hostmod_ctx, log_ctx, host_controller_address, NULL, NULL); @@ -80,81 +85,76 @@ osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, return OSD_OK; } -static char dectohex(int packet_char) +/** + * Converts decimal number to hexadecimal number + */ +static char dectohex(int dec_val) { - if (packet_char >= 10 && packet_char <= 15) - return packet_char - 10 + 'a'; - else if (packet_char >= 0 && packet_char <= 9) - return packet_char + '0'; - else - return -1; + if (dec_val >= 10 && dec_val <= 15) { + return dec_val - 10 + 'a'; + } else if (dec_val >= 0 && dec_val <= 9) { + return dec_val + '0'; + } + return '\0'; } -static int hextodec(char packet_char) +/** + * Converts hexadecimal number to decimal number + */ +static int hextodec(char hex_val) { - if (packet_char >= 'a' && packet_char <= 'f') - return packet_char + 10 - 'a'; - else if (packet_char >= 'A' && packet_char <= 'F') - return packet_char + 10 - 'A'; - else if (packet_char >= '0' && packet_char <= '9') - return packet_char - '0'; - else - return -1; + if (hex_val >= 'a' && hex_val <= 'f') { + return hex_val + 10 - 'a'; + } else if (hex_val >= 'A' && hex_val <= 'F') { + return hex_val + 10 - 'A'; + } else if (hex_val >= '0' && hex_val <= '9') { + return hex_val - '0'; + } + return -1; } API_EXPORT -osd_result osd_gdbserver_connect_hostmod(struct osd_gdbserver_ctx *ctx) +void mem2hex(uint8_t *mem_val, size_t mem_len, uint8_t *mem_hex) { - osd_result rv = osd_hostmod_connect(ctx->hostmod_ctx); - if (OSD_FAILED(rv)) { - return rv; + size_t i; + for (i = 0; i < 2 * mem_len; i++) { + uint8_t temp = (mem_val[i / 2] >> (4 * ((i + 1) % 2))) & 0x0f; + mem_hex[i] = dectohex(temp); } - - return OSD_OK; + mem_hex[i] = '\0'; } API_EXPORT -osd_result osd_gdbserver_connect_GDB(struct osd_gdbserver_ctx *ctx) +void hex2mem(uint8_t *mem_hex, size_t mem_len, uint8_t *mem_val) { - int sockoptval = 1; - - ctx->fd = socket(AF_INET, SOCK_STREAM, 0); - - if (OSD_FAILED(ctx->fd)) { - osd_gdbserver_free(&ctx); - return OSD_ERROR_CONNECTION_FAILED; - } - - setsockopt(ctx->fd, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(int)); - - memset(&ctx->sin, 0, sizeof(ctx->sin)); - ctx->sin.sin_family = AF_INET; - ctx->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - ctx->sin.sin_port = htons(OSD_GDBSERVER_PORT); - - if (OSD_FAILED( - bind(ctx->fd, (struct sockaddr *)&ctx->sin, sizeof(ctx->sin)))) { - close(ctx->fd); - osd_gdbserver_free(&ctx); - return OSD_ERROR_CONNECTION_FAILED; + size_t i; + uint8_t temp; + memset(mem_val, 0, mem_len); + for (i = 0; i < 2 * mem_len; i++) { + temp = hextodec(mem_hex[i]); + mem_val[i / 2] |= temp << (4 * ((i + 1) % 2)); } + mem_val[mem_len] = 0; +} - /* Change the socket into non-blocking state */ - fcntl(ctx->fd, F_SETFL, O_NONBLOCK); - - if (OSD_FAILED(listen(ctx->fd, 1))) { - close(ctx->fd); - osd_gdbserver_free(&ctx); - return OSD_ERROR_CONNECTION_FAILED; +API_EXPORT +void osd_gdbserver_set_port(struct osd_gdbserver_ctx *ctx, int port) +{ + if (port == -1) { + ctx->port = OSD_GDBSERVER_PORT_DEFAULT; + } else { + ctx->port = port; } - - return OSD_OK; } API_EXPORT -osd_result osd_gdbserver_disconnect_hostmod(struct osd_gdbserver_ctx *ctx) +void osd_gdbserver_set_addr(struct osd_gdbserver_ctx *ctx, int address) { - return osd_hostmod_disconnect(ctx->hostmod_ctx); + if (address == -1) { + ctx->addr = INADDR_LOOPBACK; + } else { + ctx->addr = address; + } } API_EXPORT @@ -178,113 +178,11 @@ void osd_gdbserver_free(struct osd_gdbserver_ctx **ctx_p) *ctx_p = NULL; } -static osd_result handle_rsp_packet(void *arg) -{ - struct osd_gdbserver_ctx *ctx = (struct osd_gdbserver_ctx *)arg; - osd_result rv; - - struct sockaddr_in addr_in; - addr_in.sin_port = 0; - socklen_t addr_in_size = sizeof(addr_in); - - getsockname(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); - printf("server started listening on port %d\n", ntohs(addr_in.sin_port)); - - ctx->client_fd = - accept(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); - - if (OSD_FAILED(ctx->client_fd)) { - close(ctx->client_fd); - return OSD_ERROR_FAILURE; - } - - printf("Server got connection from client %s\n", - inet_ntoa(addr_in.sin_addr)); - - /* Change the socket into non-blocking state */ - fcntl(ctx->client_fd, F_SETFL, O_NONBLOCK); - - int packet_len; - char rsp_packet_buffer[OSD_GDBSERVER_BUFF_SIZE]; - - osd_gdbserver_read_data(ctx); - - // osd_gdbserver_write_data(ctx, "+", 1); - // // rv = osd_gdbserver_read_data(ctx); - // if (OSD_FAILED(rv)) { - // return rv; - // } - - /* - rv = receive_rsp_packet(ctx, rsp_packet_buffer, &packet_len); - if (OSD_FAILED(rv)) { - return rv; - } - - if (packet_len > 0) { - switch (rsp_packet_buffer[0]) { - case '?': - break; - case 'M': - break; - case 'm': - break; - case 'g': - rv = get_registers_packet(ctx, rsp_packet_buffer, packet_len); - break; - case 'G': - break; - case 'p': - rv = get_register_packet(ctx, rsp_packet_buffer, packet_len); - break; - case 'P': - rv = set_register_packet(ctx, rsp_packet_buffer, packet_len); - case 'z': - case 'Z': - case 'c': - case 's': - default: - // Unsupported or unknown packet - osd_gdbserver_write_data(ctx, NULL, 0); - break; - } - } - */ - return OSD_OK; -} - -static void *gdbserver_thread_routine(void *arg) -{ - handle_rsp_packet(arg); - return NULL; -} - -API_EXPORT -osd_result osd_gdbserver_start(struct osd_gdbserver_ctx *ctx) -{ - osd_result rv; - - osd_gdbserver_connect_GDB(ctx); - rv = pthread_create(&osd_gdbserver_thread, 0, gdbserver_thread_routine, - (void *)ctx); - if (OSD_FAILED(rv)) { - return rv; - } - - return OSD_OK; -} - -void osd_gdbserver_stop(struct osd_gdbserver_ctx *ctx) -{ - close(ctx->fd); - pthread_join(osd_gdbserver_thread, NULL); -} - -API_EXPORT -osd_result osd_gdbserver_read_data(struct osd_gdbserver_ctx *ctx) +static osd_result osd_gdbserver_read_data(struct osd_gdbserver_ctx *ctx) { memset(ctx->buffer, 0, sizeof ctx->buffer); - ctx->buf_cnt = read(ctx->client_fd, ctx->buffer, OSD_GDBSERVER_BUFF_SIZE); + ctx->buf_cnt = + recv(ctx->client_fd, ctx->buffer, OSD_GDBSERVER_BUFF_SIZE, 0); if (OSD_FAILED(ctx->buf_cnt)) { return OSD_ERROR_CONNECTION_FAILED; @@ -303,9 +201,8 @@ osd_result osd_gdbserver_read_data(struct osd_gdbserver_ctx *ctx) return OSD_OK; } -API_EXPORT -osd_result osd_gdbserver_write_data(struct osd_gdbserver_ctx *ctx, char *data, - int len) +static osd_result osd_gdbserver_write_data(struct osd_gdbserver_ctx *ctx, + char *data, int len) { if (ctx->closed == 1) { return OSD_ERROR_NOT_CONNECTED; @@ -398,11 +295,15 @@ static osd_result receive_rsp_packet(struct osd_gdbserver_ctx *ctx, if (ver_checksum == 1) { rv = osd_gdbserver_write_data(ctx, "+", 1); + if (OSD_FAILED(rv)) { + return rv; + } } else { rv = osd_gdbserver_write_data(ctx, "-", 1); - } - if (OSD_FAILED(rv)) { - return rv; + if (OSD_FAILED(rv)) { + return rv; + } + return OSD_ERROR_FAILURE; } return OSD_OK; @@ -435,26 +336,24 @@ static osd_result send_rsp_packet(struct osd_gdbserver_ctx *ctx, char packet_buffer[packet_data_len + 5]; osd_result rv; - while (1) { - encode_rsp_packet(packet_data, packet_data_len, packet_buffer); + encode_rsp_packet(packet_data, packet_data_len, packet_buffer); - rv = osd_gdbserver_write_data(ctx, packet_buffer, packet_data_len + 4); - if (OSD_FAILED(rv)) { - return OSD_ERROR_FAILURE; - } + rv = osd_gdbserver_write_data(ctx, packet_buffer, packet_data_len + 4); + if (OSD_FAILED(rv)) { + return OSD_ERROR_FAILURE; + } - rv = osd_gdbserver_read_data(ctx); - if (OSD_FAILED(rv)) { - return OSD_ERROR_FAILURE; - } + rv = osd_gdbserver_read_data(ctx); + if (OSD_FAILED(rv)) { + return OSD_ERROR_FAILURE; + } - char reply = ctx->buffer[0]; - if (reply == '+') { - break; - } else { - return OSD_ERROR_FAILURE; - } + char reply = ctx->buffer[0]; + if (reply == '+') { + return OSD_OK; } + + return OSD_ERROR_FAILURE; } static osd_result gdb_read_general_registers_cmd(struct osd_gdbserver_ctx *ctx, @@ -606,30 +505,6 @@ static osd_result gdb_write_register_cmd(struct osd_gdbserver_ctx *ctx, return OSD_OK; } -API_EXPORT -void mem2hex(uint8_t *mem_val, size_t mem_len, uint8_t *mem_hex) -{ - size_t i; - for (i = 0; i < 2 * mem_len; i++) { - uint8_t temp = (mem_val[i / 2] >> (4 * ((i + 1) % 2))) & 0x0f; - mem_hex[i] = dectohex(temp); - } - mem_hex[i] = '\0'; -} - -API_EXPORT -void hex2mem(uint8_t *mem_hex, size_t mem_len, uint8_t *mem_val) -{ - size_t i; - uint8_t temp; - memset(mem_val, 0, mem_len); - for (i = 0; i < 2 * mem_len; i++) { - temp = hextodec(mem_hex[i]); - mem_val[i / 2] |= temp << (4 * ((i + 1) % 2)); - } - mem_val[mem_len] = 0; -} - static osd_result gdb_read_memory_cmd(struct osd_gdbserver_ctx *ctx, char *packet, int packet_len) { @@ -670,6 +545,7 @@ static osd_result gdb_read_memory_cmd(struct osd_gdbserver_ctx *ctx, } free(mem_packet_content); + return OSD_OK; } static osd_result gdb_write_memory_cmd(struct osd_gdbserver_ctx *ctx, @@ -716,4 +592,168 @@ static osd_result gdb_write_memory_cmd(struct osd_gdbserver_ctx *ctx, if (OSD_FAILED(rv)) { return rv; } + + return OSD_OK; } + +static osd_result handle_rsp_packet(void *arg) +{ + struct osd_gdbserver_ctx *ctx = (struct osd_gdbserver_ctx *)arg; + osd_result rv; + + struct sockaddr_in addr_in; + addr_in.sin_port = 0; + socklen_t addr_in_size = sizeof(addr_in); + + getsockname(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); + printf("server started listening on port %d\n", ntohs(addr_in.sin_port)); + + ctx->client_fd = + accept(ctx->fd, (struct sockaddr *)&addr_in, &addr_in_size); + + if (OSD_FAILED(ctx->client_fd)) { + close(ctx->client_fd); + return OSD_ERROR_FAILURE; + } + + printf("Server got connection from client %s\n", + inet_ntoa(addr_in.sin_addr)); + + int packet_len; + char rsp_packet_buffer[OSD_GDBSERVER_BUFF_SIZE]; + + ctx->rv = osd_gdbserver_read_data(ctx); + if (OSD_FAILED(ctx->rv)) { + return ctx->rv; + } + + ctx->rv = osd_gdbserver_write_data(ctx, "$p0007#37", 9); + if (OSD_FAILED(ctx->rv)) { + return ctx->rv; + } + // rv = osd_gdbserver_read_data(ctx); + // if (OSD_FAILED(rv)) { + // return rv; + // } + + // rv = receive_rsp_packet(ctx, rsp_packet_buffer, &packet_len); + // if (OSD_FAILED(rv)) { + // return rv; + // } + + // if (packet_len > 0) { + // switch (rsp_packet_buffer[0]) { + // case '?': + // break; + // case 'm': + // rv = gdb_read_memory_cmd(ctx, rsp_packet_buffer, packet_len); + // break; + // case 'M': + // rv = gdb_write_memory_cmd(ctx, rsp_packet_buffer, + // packet_len); + // break; + // case 'g': + // rv = gdb_read_general_registers_cmd(ctx, rsp_packet_buffer, + // packet_len); + // break; + // case 'G': + // rv = gdb_write_general_registers_cmd(ctx, rsp_packet_buffer, + // packet_len); + // break; + // case 'p': + // rv = gdb_read_register_cmd(ctx, rsp_packet_buffer, + // packet_len); + // break; + // case 'P': + // rv = gdb_write_register_cmd(ctx, rsp_packet_buffer, + // packet_len); + // break; + // case 'z': + // case 'Z': + // case 'c': + // case 's': + // default: + // // Unsupported or unknown packet + // osd_gdbserver_write_data(ctx, NULL, 0); + // break; + // } + // } + + return OSD_OK; +} + +static void *gdbserver_thread_routine(void *arg) +{ + handle_rsp_packet(arg); + struct osd_gdbserver_ctx *ctx = (struct osd_gdbserver_ctx *)arg; + pthread_exit(arg); + + return NULL; +} + +API_EXPORT +osd_result osd_gdbserver_connect(struct osd_gdbserver_ctx *ctx) +{ + osd_result rv = osd_hostmod_connect(ctx->hostmod_ctx); + if (OSD_FAILED(rv)) { + return rv; + } + + int sockoptval = 1; + + ctx->fd = socket(AF_INET, SOCK_STREAM, 0); + if (OSD_FAILED(ctx->fd)) { + osd_gdbserver_free(&ctx); + return OSD_ERROR_CONNECTION_FAILED; + } + + setsockopt(ctx->fd, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(int)); + + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(ctx->addr); + sin.sin_port = htons(ctx->port); + + rv = bind(ctx->fd, (struct sockaddr *)&sin, sizeof(sin)); + if (OSD_FAILED(rv)) { + close(ctx->fd); + osd_gdbserver_free(&ctx); + return OSD_ERROR_CONNECTION_FAILED; + } + + rv = listen(ctx->fd, 1); + if (OSD_FAILED(rv)) { + close(ctx->fd); + osd_gdbserver_free(&ctx); + return OSD_ERROR_CONNECTION_FAILED; + } + + // Used for sending/receiving data between GDB and OSD-GDB server + rv = pthread_create(&osd_gdbserver_thread, 0, gdbserver_thread_routine, + (void *)ctx); + if (OSD_FAILED(rv)) { + return rv; + } + + return OSD_OK; +} + +API_EXPORT +osd_result osd_gdbserver_disconnect(struct osd_gdbserver_ctx *ctx) +{ + close(ctx->fd); + pthread_join(osd_gdbserver_thread, (void *)&ctx); + + ctx = ((struct osd_gdbserver_ctx *)ctx); + if (OSD_FAILED(ctx->rv)) { + return ctx->rv; + } + + osd_result rv = osd_hostmod_disconnect(ctx->hostmod_ctx); + if (OSD_FAILED(rv)) { + return rv; + } + + return OSD_OK; +} \ No newline at end of file diff --git a/src/libosd/include/osd/gdbserver.h b/src/libosd/include/osd/gdbserver.h index d6b9233..a48ef1c 100644 --- a/src/libosd/include/osd/gdbserver.h +++ b/src/libosd/include/osd/gdbserver.h @@ -16,8 +16,6 @@ #ifndef OSD_GDBSERVER_H #define OSD_GDBSERVER_H -#include -#include #include #include @@ -37,17 +35,25 @@ extern "C" { struct osd_gdbserver_ctx; /** - * Indicates the port for connecting to GDB + * Indicates the default port for connecting to GDB */ -#define OSD_GDBSERVER_PORT 5555 +#define OSD_GDBSERVER_PORT_DEFAULT 5555 /** - * Indicates the size of the buffer + * Indicates the size of the RSP packet buffer */ #define OSD_GDBSERVER_BUFF_SIZE 1024 /** * Create a new context object + * + * @param ctx context object + * @param log_ctx logging context + * @param host_controller_address ZeroMQ endpoint of the host controller + * @param cdm_di_addr DI address of the CDM module + * @param mam_di_addr DI address of the MAM module + * @return OSD_OK if initialization was successful, + * any other return code indicates an error */ osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, struct osd_log_ctx *log_ctx, @@ -55,63 +61,61 @@ osd_result osd_gdbserver_new(struct osd_gdbserver_ctx **ctx, uint16_t cdm_di_addr, uint16_t mam_di_addr); /** - * Connect GDB server to the GDB client + * Connect to the GDB client and the host controller * + * @param ctx the osd_gdbserver_ctx context object * @return OSD_OK on success, any other value indicates an error + * + * @see osd_gdbserver_disconnect() */ -osd_result osd_gdbserver_connect_GDB(struct osd_gdbserver_ctx *ctx); +osd_result osd_gdbserver_connect(struct osd_gdbserver_ctx *ctx); /** - * Connect GDB server to the host controller + * Stop the connection with GDB client and the host controller * + * @param ctx the osd_gdbserver_ctx context object * @return OSD_OK on success, any other value indicates an error + * + * @see osd_gdbserver_connect() */ -osd_result osd_gdbserver_connect_hostmod(struct osd_gdbserver_ctx *ctx); - -/** - * @copydoc osd_hostmod_disconnect() - */ -osd_result osd_gdbserver_disconnect_hostmod(struct osd_gdbserver_ctx *ctx); - -/** - * @copydoc osd_hostmod_is_connected() - */ -bool osd_gdbserver_is_connected_hostmod(struct osd_gdbserver_ctx *ctx); +osd_result osd_gdbserver_disconnect(struct osd_gdbserver_ctx *ctx); /** * Free the context object + * + * By calling this function all resources associated with the context object + * are freed and the ctx_p itself is NULLed. + * + * @param ctx_p the context object */ void osd_gdbserver_free(struct osd_gdbserver_ctx **ctx_p); /** - * Start the connection with GDB client + * @copydoc osd_hostmod_is_connected() */ -osd_result osd_gdbserver_start(struct osd_gdbserver_ctx *ctx); +bool osd_gdbserver_is_connected_hostmod(struct osd_gdbserver_ctx *ctx); /** - * Stop the connection with GDB client - */ -void osd_gdbserver_stop(struct osd_gdbserver_ctx *ctx); -/** - * Read data from the GDB client + * Set the port number for TCP communication with GDB * + * @param ctx the context object + * @param port the port number the server will bind to * @return OSD_OK on success, any other value indicates an error * - * @see osd_gdbserver_write_data() + * @see osd_gdbserver_set_addr() */ -osd_result osd_gdbserver_read_data(struct osd_gdbserver_ctx *ctx); +void osd_gdbserver_set_port(struct osd_gdbserver_ctx *ctx, int port); /** - * Write data to the GDB client + * Set the IP address for TCP communication with GDB * - * @param data the data to be written to the connected client - * @param len the length of the data to be written + * @param ctx the context object + * @param address the address the server will bind to * @return OSD_OK on success, any other value indicates an error * - * @see osd_gdbserver_read_data() + * @see osd_gdbserver_set_port() */ -osd_result osd_gdbserver_write_data(struct osd_gdbserver_ctx *ctx, char *data, - int len); +void osd_gdbserver_set_addr(struct osd_gdbserver_ctx *ctx, int address); /**@}*/ /* end of doxygen group libosd-gdbserver */ diff --git a/tests/unit/check_gdbserver.c b/tests/unit/check_gdbserver.c index 82bd4f0..4f263eb 100644 --- a/tests/unit/check_gdbserver.c +++ b/tests/unit/check_gdbserver.c @@ -40,39 +40,44 @@ pthread_t mock_gdbserver_thread; volatile int mock_gdbserver_ready; volatile int mock_server_thread_cancel; -void setup_hostmod(void) +void setup_gdbserver(void) { osd_result rv; log_ctx = testutil_get_log_ctx(); - // initialize module context + // initialize OSD-GDB server module context rv = osd_gdbserver_new(&gdbserver_ctx, log_ctx, "inproc://testing", mock_cdm_diaddr, mock_mam_diaddr); + osd_gdbserver_set_port(gdbserver_ctx, 5555); + osd_gdbserver_set_addr(gdbserver_ctx, -1); ck_assert_int_eq(rv, OSD_OK); ck_assert_ptr_ne(gdbserver_ctx, NULL); + // initialize mock GDB client module context rv = mock_gdbclient_new(&gdbclient_ctx); ck_assert_int_eq(rv, OSD_OK); + mock_gdbclient_set_port(gdbclient_ctx, 5555); + mock_gdbclient_set_addr(gdbclient_ctx, "127.0.0.1"); // connect mock_host_controller_expect_diaddr_req(mock_hostmod_diaddr); - rv = osd_gdbserver_connect_hostmod(gdbserver_ctx); + osd_gdbserver_connect(gdbserver_ctx); ck_assert_int_eq(rv, OSD_OK); } -void teardown_hostmod(void) +void teardown_gdbserver(void) { osd_result rv; - rv = osd_gdbserver_disconnect_hostmod(gdbserver_ctx); + rv = osd_gdbserver_disconnect(gdbserver_ctx); ck_assert_int_eq(rv, OSD_OK); } /** - * Test fixture: setup (called before each tests) + * Test fixture: setup (called before each test) */ void setup(void) { @@ -81,13 +86,14 @@ void setup(void) mock_hostmod_diaddr = osd_diaddr_build(1, 1); mock_cdm_diaddr = osd_diaddr_build(target_subnet_addr, 5); mock_mam_diaddr = osd_diaddr_build(target_subnet_addr, 10); + mock_host_controller_setup(); - setup_hostmod(); + setup_gdbserver(); - rv = osd_gdbserver_start(gdbserver_ctx); - ck_assert_int_eq(rv, OSD_OK); + rv = osd_gdbserver_is_connected_hostmod(gdbserver_ctx); + ck_assert_uint_eq(rv, 1); - rv = mock_gdbclient_connect(gdbclient_ctx); + rv = mock_gdbclient_start(gdbclient_ctx); ck_assert_int_eq(rv, OSD_OK); } @@ -96,12 +102,12 @@ void setup(void) */ void teardown(void) { + osd_result rv; + mock_host_controller_wait_for_event_tx(); - teardown_hostmod(); + teardown_gdbserver(); mock_host_controller_teardown(); - osd_gdbserver_stop(gdbserver_ctx); - osd_gdbserver_free(&gdbserver_ctx); ck_assert_ptr_eq(gdbserver_ctx, NULL); @@ -296,7 +302,7 @@ Suite *suite(void) tc_init = tcase_create("Init"); tcase_add_test(tc_init, test_init_base); - tcase_set_timeout(tc_init, 120); + tcase_set_timeout(tc_init, 60); suite_add_tcase(s, tc_init); tc_testing = tcase_create("Testing"); diff --git a/tests/unit/mock_gdbclient.c b/tests/unit/mock_gdbclient.c index 2046838..17ae2c8 100644 --- a/tests/unit/mock_gdbclient.c +++ b/tests/unit/mock_gdbclient.c @@ -41,34 +41,83 @@ osd_result mock_gdbclient_connect(struct mock_gdbclient_ctx *ctx) { osd_result rv; - struct sockaddr_in gdb_serv_addr; + struct sockaddr_in gdb_server_addr; if (OSD_FAILED(ctx->fd = socket(AF_INET, SOCK_STREAM, 0))) { return OSD_ERROR_CONNECTION_FAILED; } - memset(&gdb_serv_addr, '0', sizeof(gdb_serv_addr)); + memset(&gdb_server_addr, '0', sizeof(gdb_server_addr)); - gdb_serv_addr.sin_family = AF_INET; - gdb_serv_addr.sin_port = htons(MOCK_GDBCLIENT_PORT); + gdb_server_addr.sin_family = AF_INET; + gdb_server_addr.sin_port = htons(ctx->port); - if (OSD_FAILED(inet_pton(AF_INET, "127.0.0.1", &gdb_serv_addr.sin_addr))) { + if (OSD_FAILED(inet_pton(AF_INET, ctx->addr, &gdb_server_addr.sin_addr))) { return OSD_ERROR_CONNECTION_FAILED; } - if (OSD_FAILED(connect(ctx->fd, (struct sockaddr *)&gdb_serv_addr, - sizeof(gdb_serv_addr)))) { + if (OSD_FAILED(connect(ctx->fd, (struct sockaddr *)&gdb_server_addr, + sizeof(gdb_server_addr)))) { return OSD_ERROR_CONNECTION_FAILED; } - mock_gdbclient_write_data(ctx, "OK", 2); + return OSD_OK; +} + +osd_result mock_gdbclient_start(struct mock_gdbclient_ctx *ctx) +{ + osd_result rv; + + rv = mock_gdbclient_connect(ctx); + if (OSD_FAILED(rv)) { + return rv; + } + + rv = mock_gdbclient_handle_resp(ctx); + if (OSD_FAILED(rv)) { + return rv; + } + + return OSD_OK; +} + +void mock_gdbclient_set_port(struct mock_gdbclient_ctx *ctx, int port) +{ + if (port == -1) { + ctx->port = MOCK_GDBCLIENT_PORT_DEFAULT; + } else { + ctx->port = port; + } +} + +void mock_gdbclient_set_addr(struct mock_gdbclient_ctx *ctx, char *address) +{ + if (address == NULL) { + ctx->addr = "127.0.0.1"; + } else { + ctx->addr = address; + } +} + +osd_result mock_gdbclient_handle_resp(struct mock_gdbclient_ctx *ctx) +{ + osd_result rv; + rv = mock_gdbclient_write_data(ctx, "$p0007#37", 9); + if (OSD_FAILED(rv)) { + return rv; + } + + rv = mock_gdbclient_read_data(ctx); + if (OSD_FAILED(rv)) { + return rv; + } return OSD_OK; } osd_result mock_gdbclient_read_data(struct mock_gdbclient_ctx *ctx) { - // memset(ctx->buffer, 0, sizeof ctx->buffer); + memset(ctx->buffer, 0, sizeof ctx->buffer); ctx->buf_cnt = read(ctx->fd, ctx->buffer, MOCK_GDBCLIENT_BUFF_SIZE); if (OSD_FAILED(ctx->buf_cnt)) { diff --git a/tests/unit/mock_gdbclient.h b/tests/unit/mock_gdbclient.h index 75294a9..578505f 100644 --- a/tests/unit/mock_gdbclient.h +++ b/tests/unit/mock_gdbclient.h @@ -21,17 +21,23 @@ #include #include -#define MOCK_GDBCLIENT_PORT 5555 +#define MOCK_GDBCLIENT_PORT_DEFAULT 5555 #define MOCK_GDBCLIENT_BUFF_SIZE 1024 struct mock_gdbclient_ctx { int fd; int closed; int buf_cnt; + int port; + char *addr; char buffer[MOCK_GDBCLIENT_BUFF_SIZE]; }; osd_result mock_gdbclient_new(struct mock_gdbclient_ctx **ctx); +osd_result mock_gdbclient_start(struct mock_gdbclient_ctx *ctx); +void mock_gdbclient_set_port(struct mock_gdbclient_ctx *ctx, int port); +void mock_gdbclient_set_addr(struct mock_gdbclient_ctx *ctx, char *address); +osd_result mock_gdbclient_handle_resp(struct mock_gdbclient_ctx *ctx); osd_result mock_gdbclient_connect(struct mock_gdbclient_ctx *ctx); osd_result mock_gdbclient_write_data(struct mock_gdbclient_ctx *ctx, char *data, int len);