-/***************************************************************************
- * Copyright (C) 2005 by Dominic Rath *
- * Dominic.Rath@gmx.de *
- * *
- * part of this file is taken from libcli (libcli.sourceforge.net) *
- * Copyright (C) David Parrish (david@dparrish.com) *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the *
- * Free Software Foundation, Inc., *
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
- ***************************************************************************/
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "replacements.h"
-
-#include "command.h"
-
-#include "log.h"
-#include "time_support.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <unistd.h>
-
-void command_print_help_line(command_context_t* context, struct command_s *command, int indent);
-
-int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
-int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
-
-int build_unique_lengths(command_context_t *context, command_t *commands)
-{
- command_t *c, *p;
-
- /* iterate through all commands */
- for (c = commands; c; c = c->next)
- {
- /* find out how many characters are required to uniquely identify a command */
- for (c->unique_len = 1; c->unique_len <= strlen(c->name); c->unique_len++)
- {
- int foundmatch = 0;
-
- /* for every command, see if the current length is enough */
- for (p = commands; p; p = p->next)
- {
- /* ignore the command itself */
- if (c == p)
- continue;
-
- /* compare commands up to the current length */
- if (strncmp(p->name, c->name, c->unique_len) == 0)
- foundmatch++;
- }
-
- /* when none of the commands matched, we've found the minimum length required */
- if (!foundmatch)
- break;
- }
-
- /* if the current command has children, build the unique lengths for them */
- if (c->children)
- build_unique_lengths(context, c->children);
- }
-
- return ERROR_OK;
-}
-
-/* Avoid evaluating this each time we add a command. Reduces overhead from O(n^2) to O(n).
- * Makes a difference on ARM7 types machines and is not observable on GHz machines.
- */
-static int unique_length_dirty = 1;
-
-command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help)
-{
- command_t *c, *p;
- unique_length_dirty = 1;
-
- if (!context || !name)
- return NULL;
-
- c = malloc(sizeof(command_t));
-
- c->name = strdup(name);
- c->parent = parent;
- c->children = NULL;
- c->handler = handler;
- c->mode = mode;
- if (help)
- c->help = strdup(help);
- else
- c->help = NULL;
- c->unique_len = 0;
- c->next = NULL;
-
- /* place command in tree */
- if (parent)
- {
- if (parent->children)
- {
- /* find last child */
- for (p = parent->children; p && p->next; p = p->next);
- if (p)
- p->next = c;
- }
- else
- {
- parent->children = c;
- }
- }
- else
- {
- if (context->commands)
- {
- /* find last command */
- for (p = context->commands; p && p->next; p = p->next);
- if (p)
- p->next = c;
- }
- else
- {
- context->commands = c;
- }
- }
-
- return c;
-}
-
-int unregister_command(command_context_t *context, char *name)
-{
- unique_length_dirty = 1;
-
- command_t *c, *p = NULL, *c2;
-
- if ((!context) || (!name))
- return ERROR_INVALID_ARGUMENTS;
-
- /* find command */
- for (c = context->commands; c; c = c->next)
- {
- if (strcmp(name, c->name) == 0)
- {
- /* unlink command */
- if (p)
- {
- p->next = c->next;
- }
- else
- {
- context->commands = c->next;
- }
-
- /* unregister children */
- if (c->children)
- {
- for (c2 = c->children; c2; c2 = c2->next)
- {
- free(c2->name);
- if (c2->help)
- free(c2->help);
- free(c2);
- }
- }
-
- /* delete command */
- free(c->name);
- if (c->help)
- free(c->help);
- free(c);
- }
-
- /* remember the last command for unlinking */
- p = c;
- }
-
- return ERROR_OK;
-}
-
-int parse_line(char *line, char *words[], int max_words)
-{
- int nwords = 0;
- char *p = line;
- char *word_start = line;
- int inquote = 0;
-
- while (nwords < max_words - 1)
- {
- /* check if we reached
- * a terminating NUL
- * a matching closing quote character " or '
- * we're inside a word but not a quote, and the current character is whitespace
- */
- if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))
- {
- /* we're inside a word or quote, and reached its end*/
- if (word_start)
- {
- int len;
- char *word_end=p;
-
- /* This will handle extra whitespace within quotes */
- while (isspace(*word_start)&&(word_start<word_end))
- word_start++;
- while (isspace(*(word_end-1))&&(word_start<word_end))
- word_end--;
- len = word_end - word_start;
-
- if (len>0)
- {
- /* copy the word */
- memcpy(words[nwords] = malloc(len + 1), word_start, len);
- /* add terminating NUL */
- words[nwords++][len] = 0;
- }
- }
- /* we're done parsing the line */
- if (!*p)
- break;
-
- /* skip over trailing quote or whitespace*/
- if (inquote || isspace(*p))
- p++;
- while (isspace(*p))
- p++;
-
- inquote = 0;
- word_start = 0;
- }
- else if (*p == '"' || *p == '\'')
- {
- /* we've reached the beginning of a quote */
- inquote = *p++;
- word_start = p;
- }
- else
- {
- /* we've reached the beginning of a new word */
- if (!word_start)
- word_start = p;
-
- /* normal character, skip */
- p++;
- }
- }
-
- return nwords;
-}
-
-void command_print(command_context_t *context, char *format, ...)
-{
- char *buffer = NULL;
- int n, size = 0;
- char *p;
-
- /* process format string */
- for (;;)
- {
- va_list ap;
- va_start(ap, format);
- if (!buffer || (n = vsnprintf(buffer, size, format, ap)) >= size)
- {
- /* increase buffer until it fits the whole string */
- if (!(p = realloc(buffer, size += 4096)))
- {
- /* gotta free up */
- if (buffer)
- free(buffer);
- va_end(ap);
- return;
- }
-
- buffer = p;
-
- va_end(ap);
- continue;
- }
- va_end(ap);
- break;
- }
-
- /* vsnprintf failed */
- if (n < 0)
- {
- if (buffer)
- free(buffer);
- return;
- }
-
- p = buffer;
-
- /* process lines in buffer */
- do {
- char *next = strchr(p, '\n');
-
- if (next)
- *next++ = 0;
-
- if (context->output_handler)
- context->output_handler(context, p);
-
- p = next;
- } while (p);
-
- if (buffer)
- free(buffer);
-}
-
-int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word)
-{
- command_t *c;
-
- if (unique_length_dirty)
- {
- unique_length_dirty = 0;
- /* update unique lengths */
- build_unique_lengths(context, context->commands);
- }
-
- for (c = commands; c; c = c->next)
- {
- if (strncasecmp(c->name, words[start_word], c->unique_len))
- continue;
-
- if (strncasecmp(c->name, words[start_word], strlen(words[start_word])))
- continue;
-
- if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
- {
- if (!c->children)
- {
- if (!c->handler)
- {
- command_print(context, "No handler for command");
- break;
- }
- else
- {
- int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
- if (retval == ERROR_COMMAND_SYNTAX_ERROR)
- {
- command_print(context, "Syntax error:");
- command_print_help_line(context, c, 0);
- }
- return retval;
- }
- }
- else
- {
- if (start_word == num_words - 1)
- {
- command_print(context, "Incomplete command");
- break;
- }
- return find_and_run_command(context, c->children, words, num_words, start_word + 1);
- }
- }
- }
-
- command_print(context, "Command %s not found", words[start_word]);
- return ERROR_OK;
-}
-
-static int command_run_line_inner(command_context_t *context, char *line)
-{
- int nwords;
- char *words[128] = {0};
- int retval;
- int i;
-
- if ((!context) || (!line))
- return ERROR_INVALID_ARGUMENTS;
-
- /* skip preceding whitespace */
- while (isspace(*line))
- line++;
-
- /* empty line, ignore */
- if (!*line)
- return ERROR_OK;
-
- /* ignore comments */
- if (*line && (line[0] == '#'))
- return ERROR_OK;
-
- if (context->echo)
- {
- command_print(context, "%s", line);
- }
-
- nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
-
- if (nwords > 0)
- retval = find_and_run_command(context, context->commands, words, nwords, 0);
- else
- return ERROR_INVALID_ARGUMENTS;
-
- for (i = 0; i < nwords; i++)
- free(words[i]);
-
- return retval;
-}
-
-int command_run_line(command_context_t *context, char *line)
-{
- int retval=command_run_line_inner(context, line);
- // we don't want any dangling callbacks!
- //
- // Capturing output from logging is *very* loosly modeled on C/C++ exceptions.
- // the capture must be set up at function entry and
- // stops when the function call returns
- log_setCallback(NULL, NULL);
- return retval;
-}
-int command_run_file(command_context_t *context, FILE *file, enum command_mode mode)
-{
- int retval = ERROR_OK;
- int old_command_mode;
- char *buffer=malloc(4096);
- if (buffer==NULL)
- {
- return ERROR_INVALID_ARGUMENTS;
- }
-
- old_command_mode = context->mode;
- context->mode = mode;
-
- while (fgets(buffer, 4096, file))
- {
- char *p;
- char *cmd, *end;
-
- /* stop processing line after a comment (#, !) or a LF, CR were encountered */
- if ((p = strpbrk(buffer, "#!\r\n")))
- *p = 0;
-
- /* skip over leading whitespace */
- cmd = buffer;
- while (isspace(*cmd))
- cmd++;
-
- /* empty (all whitespace) line? */
- if (!*cmd)
- continue;
-
- /* search the end of the current line, ignore trailing whitespace */
- for (p = end = cmd; *p; p++)
- if (!isspace(*p))
- end = p;
-
- /* terminate end */
- *++end = 0;
- if (strcasecmp(cmd, "quit") == 0)
- break;
-
- /* run line */
- if ((retval = command_run_line_inner(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION)
- break;
- }
-
- context->mode = old_command_mode;
-
-
- free(buffer);
-
- return retval;
-}
-
-void command_print_help_line(command_context_t* context, struct command_s *command, int indent)
-{
- command_t *c;
- char indents[32] = {0};
- char *help = "no help available";
- char name_buf[64];
- int i;
-
- for (i = 0; i < indent; i+=2)
- {
- indents[i*2] = ' ';
- indents[i*2+1] = '-';
- }
- indents[i*2] = 0;
-
- if (command->help)
- help = command->help;
-
- snprintf(name_buf, 64, command->name);
- strncat(name_buf, indents, 64);
- command_print(context, "%20s\t%s", name_buf, help);
-
- if (command->children)
- {
- for (c = command->children; c; c = c->next)
- {
- command_print_help_line(context, c, indent + 1);
- }
- }
-}
-
-int command_print_help(command_context_t* context, char* name, char** args, int argc)
-{
- command_t *c;
-
- for (c = context->commands; c; c = c->next)
- {
- if (argc == 1)
- {
- if (strncasecmp(c->name, args[0], c->unique_len))
- continue;
-
- if (strncasecmp(c->name, args[0], strlen(args[0])))
- continue;
- }
-
- command_print_help_line(context, c, 0);
- }
-
- return ERROR_OK;
-}
-
-void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv)
-{
- context->output_handler = output_handler;
- context->output_handler_priv = priv;
-}
-
-command_context_t* copy_command_context(command_context_t* context)
-{
- command_context_t* copy_context = malloc(sizeof(command_context_t));
-
- *copy_context = *context;
-
- return copy_context;
-}
-
-int command_done(command_context_t *context)
-{
- free(context);
- context = NULL;
-
- return ERROR_OK;
-}
-
-command_context_t* command_init()
-{
- command_context_t* context = malloc(sizeof(command_context_t));
-
- context->mode = COMMAND_EXEC;
- context->commands = NULL;
- context->current_target = 0;
- context->echo = 0;
- context->output_handler = NULL;
- context->output_handler_priv = NULL;
-
- register_command(context, NULL, "help", command_print_help,
- COMMAND_EXEC, "display this help");
-
- register_command(context, NULL, "sleep", handle_sleep_command,
- COMMAND_ANY, "sleep for <n> milliseconds");
-
- register_command(context, NULL, "time", handle_time_command,
- COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
-
- return context;
-}
-
-/* sleep command sleeps for <n> miliseconds
- * this is useful in target startup scripts
- */
-int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- unsigned long duration = 0;
-
- if (argc == 1)
- {
- duration = strtoul(args[0], NULL, 0);
- usleep(duration * 1000);
- }
-
- return ERROR_OK;
-}
-
-int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- if (argc<1)
- return ERROR_COMMAND_SYNTAX_ERROR;
-
- duration_t duration;
- char *duration_text;
- int retval;
-
- duration_start_measure(&duration);
-
- retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc, 0);
-
- duration_stop_measure(&duration, &duration_text);
-
- float t=duration.duration.tv_sec;
- t+=((float)duration.duration.tv_usec / 1000000.0);
- command_print(cmd_ctx, "%s took %fs", args[0], t);
-
- free(duration_text);
-
- return retval;
-}
+/***************************************************************************\r
+ * Copyright (C) 2005 by Dominic Rath *\r
+ * Dominic.Rath@gmx.de *\r
+ * *\r
+ * part of this file is taken from libcli (libcli.sourceforge.net) *\r
+ * Copyright (C) David Parrish (david@dparrish.com) *\r
+ * *\r
+ * This program is free software; you can redistribute it and/or modify *\r
+ * it under the terms of the GNU General Public License as published by *\r
+ * the Free Software Foundation; either version 2 of the License, or *\r
+ * (at your option) any later version. *\r
+ * *\r
+ * This program is distributed in the hope that it will be useful, *\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *\r
+ * GNU General Public License for more details. *\r
+ * *\r
+ * You should have received a copy of the GNU General Public License *\r
+ * along with this program; if not, write to the *\r
+ * Free Software Foundation, Inc., *\r
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *\r
+ ***************************************************************************/\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#include "replacements.h"\r
+\r
+#include "command.h"\r
+\r
+#include "log.h"\r
+#include "time_support.h"\r
+\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <stdarg.h>\r
+#include <stdio.h>\r
+#include <unistd.h>\r
+\r
+void command_print_help_line(command_context_t* context, struct command_s *command, int indent);\r
+\r
+int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
+int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
+\r
+int build_unique_lengths(command_context_t *context, command_t *commands)\r
+{\r
+ command_t *c, *p;\r
+\r
+ /* iterate through all commands */\r
+ for (c = commands; c; c = c->next)\r
+ {\r
+ /* find out how many characters are required to uniquely identify a command */\r
+ for (c->unique_len = 1; c->unique_len <= strlen(c->name); c->unique_len++)\r
+ {\r
+ int foundmatch = 0;\r
+ \r
+ /* for every command, see if the current length is enough */\r
+ for (p = commands; p; p = p->next)\r
+ {\r
+ /* ignore the command itself */\r
+ if (c == p)\r
+ continue;\r
+ \r
+ /* compare commands up to the current length */\r
+ if (strncmp(p->name, c->name, c->unique_len) == 0)\r
+ foundmatch++;\r
+ }\r
+ \r
+ /* when none of the commands matched, we've found the minimum length required */\r
+ if (!foundmatch)\r
+ break;\r
+ }\r
+ \r
+ /* if the current command has children, build the unique lengths for them */\r
+ if (c->children)\r
+ build_unique_lengths(context, c->children);\r
+ }\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+/* Avoid evaluating this each time we add a command. Reduces overhead from O(n^2) to O(n). \r
+ * Makes a difference on ARM7 types machines and is not observable on GHz machines.\r
+ */\r
+static int unique_length_dirty = 1; \r
+\r
+command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help)\r
+{\r
+ command_t *c, *p;\r
+ unique_length_dirty = 1;\r
+ \r
+ if (!context || !name)\r
+ return NULL;\r
+ \r
+ c = malloc(sizeof(command_t));\r
+ \r
+ c->name = strdup(name);\r
+ c->parent = parent;\r
+ c->children = NULL;\r
+ c->handler = handler;\r
+ c->mode = mode;\r
+ if (help)\r
+ c->help = strdup(help);\r
+ else\r
+ c->help = NULL;\r
+ c->unique_len = 0;\r
+ c->next = NULL;\r
+ \r
+ /* place command in tree */\r
+ if (parent)\r
+ {\r
+ if (parent->children)\r
+ {\r
+ /* find last child */\r
+ for (p = parent->children; p && p->next; p = p->next);\r
+ if (p)\r
+ p->next = c;\r
+ }\r
+ else\r
+ {\r
+ parent->children = c;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (context->commands)\r
+ {\r
+ /* find last command */\r
+ for (p = context->commands; p && p->next; p = p->next);\r
+ if (p)\r
+ p->next = c;\r
+ }\r
+ else\r
+ {\r
+ context->commands = c;\r
+ }\r
+ }\r
+ \r
+ return c;\r
+}\r
+\r
+int unregister_command(command_context_t *context, char *name)\r
+{\r
+ unique_length_dirty = 1;\r
+ \r
+ command_t *c, *p = NULL, *c2;\r
+ \r
+ if ((!context) || (!name))\r
+ return ERROR_INVALID_ARGUMENTS;\r
+ \r
+ /* find command */\r
+ for (c = context->commands; c; c = c->next)\r
+ {\r
+ if (strcmp(name, c->name) == 0)\r
+ {\r
+ /* unlink command */\r
+ if (p)\r
+ {\r
+ p->next = c->next;\r
+ }\r
+ else\r
+ {\r
+ context->commands = c->next;\r
+ }\r
+ \r
+ /* unregister children */\r
+ if (c->children)\r
+ {\r
+ for (c2 = c->children; c2; c2 = c2->next)\r
+ {\r
+ free(c2->name);\r
+ if (c2->help)\r
+ free(c2->help);\r
+ free(c2);\r
+ }\r
+ }\r
+ \r
+ /* delete command */\r
+ free(c->name);\r
+ if (c->help)\r
+ free(c->help);\r
+ free(c);\r
+ }\r
+ \r
+ /* remember the last command for unlinking */\r
+ p = c;\r
+ }\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+int parse_line(char *line, char *words[], int max_words)\r
+{\r
+ int nwords = 0;\r
+ char *p = line;\r
+ char *word_start = line;\r
+ int inquote = 0;\r
+\r
+ while (nwords < max_words - 1)\r
+ {\r
+ /* check if we reached\r
+ * a terminating NUL\r
+ * a matching closing quote character " or '\r
+ * we're inside a word but not a quote, and the current character is whitespace\r
+ */\r
+ if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))\r
+ {\r
+ /* we're inside a word or quote, and reached its end*/\r
+ if (word_start)\r
+ {\r
+ int len;\r
+ char *word_end=p;\r
+ \r
+ /* This will handle extra whitespace within quotes */\r
+ while (isspace(*word_start)&&(word_start<word_end))\r
+ word_start++;\r
+ while (isspace(*(word_end-1))&&(word_start<word_end))\r
+ word_end--;\r
+ len = word_end - word_start;\r
+ \r
+ if (len>0)\r
+ {\r
+ /* copy the word */\r
+ memcpy(words[nwords] = malloc(len + 1), word_start, len);\r
+ /* add terminating NUL */\r
+ words[nwords++][len] = 0;\r
+ }\r
+ }\r
+ /* we're done parsing the line */\r
+ if (!*p)\r
+ break;\r
+\r
+ /* skip over trailing quote or whitespace*/\r
+ if (inquote || isspace(*p))\r
+ p++;\r
+ while (isspace(*p))\r
+ p++;\r
+ \r
+ inquote = 0;\r
+ word_start = 0;\r
+ }\r
+ else if (*p == '"' || *p == '\'')\r
+ {\r
+ /* we've reached the beginning of a quote */\r
+ inquote = *p++;\r
+ word_start = p;\r
+ }\r
+ else\r
+ {\r
+ /* we've reached the beginning of a new word */\r
+ if (!word_start)\r
+ word_start = p;\r
+ \r
+ /* normal character, skip */\r
+ p++;\r
+ }\r
+ }\r
+ \r
+ return nwords;\r
+}\r
+\r
+void command_print(command_context_t *context, char *format, ...)\r
+{\r
+ char *buffer = NULL;\r
+ int n, size = 0;\r
+ char *p;\r
+\r
+ /* process format string */\r
+ for (;;)\r
+ {\r
+ va_list ap;\r
+ va_start(ap, format);\r
+ if (!buffer || (n = vsnprintf(buffer, size, format, ap)) >= size)\r
+ {\r
+ /* increase buffer until it fits the whole string */\r
+ if (!(p = realloc(buffer, size += 4096)))\r
+ {\r
+ /* gotta free up */\r
+ if (buffer)\r
+ free(buffer);\r
+ va_end(ap);\r
+ return;\r
+ }\r
+ \r
+ buffer = p;\r
+ \r
+ va_end(ap);\r
+ continue;\r
+ }\r
+ va_end(ap);\r
+ break;\r
+ }\r
+ \r
+ /* vsnprintf failed */\r
+ if (n < 0)\r
+ {\r
+ if (buffer)\r
+ free(buffer);\r
+ return;\r
+ }\r
+\r
+ p = buffer;\r
+ \r
+ /* process lines in buffer */\r
+ do {\r
+ char *next = strchr(p, '\n');\r
+ \r
+ if (next)\r
+ *next++ = 0;\r
+\r
+ if (context->output_handler)\r
+ context->output_handler(context, p);\r
+\r
+ p = next;\r
+ } while (p);\r
+ \r
+ if (buffer)\r
+ free(buffer);\r
+}\r
+\r
+int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word)\r
+{\r
+ command_t *c;\r
+ \r
+ if (unique_length_dirty)\r
+ {\r
+ unique_length_dirty = 0;\r
+ /* update unique lengths */\r
+ build_unique_lengths(context, context->commands);\r
+ }\r
+ \r
+ for (c = commands; c; c = c->next)\r
+ {\r
+ if (strncasecmp(c->name, words[start_word], c->unique_len))\r
+ continue;\r
+\r
+ if (strncasecmp(c->name, words[start_word], strlen(words[start_word])))\r
+ continue;\r
+ \r
+ if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )\r
+ {\r
+ if (!c->children)\r
+ {\r
+ if (!c->handler)\r
+ {\r
+ command_print(context, "No handler for command");\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);\r
+ if (retval == ERROR_COMMAND_SYNTAX_ERROR)\r
+ {\r
+ command_print(context, "Syntax error:");\r
+ command_print_help_line(context, c, 0);\r
+ }\r
+ return retval; \r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (start_word == num_words - 1)\r
+ {\r
+ command_print(context, "Incomplete command");\r
+ break;\r
+ }\r
+ return find_and_run_command(context, c->children, words, num_words, start_word + 1);\r
+ }\r
+ }\r
+ }\r
+ \r
+ command_print(context, "Command %s not found", words[start_word]);\r
+ return ERROR_OK;\r
+}\r
+\r
+int command_run_line(command_context_t *context, char *line)\r
+{\r
+ int nwords;\r
+ char *words[128] = {0};\r
+ int retval;\r
+ int i;\r
+ \r
+ if ((!context) || (!line))\r
+ return ERROR_INVALID_ARGUMENTS;\r
+ \r
+ /* skip preceding whitespace */\r
+ while (isspace(*line))\r
+ line++;\r
+ \r
+ /* empty line, ignore */\r
+ if (!*line)\r
+ return ERROR_OK;\r
+ \r
+ /* ignore comments */\r
+ if (*line && (line[0] == '#'))\r
+ return ERROR_OK;\r
+ \r
+ if (context->echo)\r
+ {\r
+ command_print(context, "%s", line);\r
+ }\r
+\r
+ nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));\r
+ \r
+ if (nwords > 0)\r
+ retval = find_and_run_command(context, context->commands, words, nwords, 0);\r
+ else\r
+ return ERROR_INVALID_ARGUMENTS;\r
+ \r
+ for (i = 0; i < nwords; i++)\r
+ free(words[i]);\r
+ \r
+ return retval;\r
+}\r
+\r
+int command_run_file(command_context_t *context, FILE *file, enum command_mode mode)\r
+{\r
+ int retval = ERROR_OK;\r
+ int old_command_mode;\r
+ char *buffer=malloc(4096);\r
+ if (buffer==NULL)\r
+ {\r
+ return ERROR_INVALID_ARGUMENTS;\r
+ }\r
+ \r
+ old_command_mode = context->mode;\r
+ context->mode = mode;\r
+ \r
+ while (fgets(buffer, 4096, file))\r
+ {\r
+ char *p;\r
+ char *cmd, *end;\r
+ \r
+ /* stop processing line after a comment (#, !) or a LF, CR were encountered */\r
+ if ((p = strpbrk(buffer, "#!\r\n")))\r
+ *p = 0;\r
+\r
+ /* skip over leading whitespace */\r
+ cmd = buffer;\r
+ while (isspace(*cmd))\r
+ cmd++;\r
+\r
+ /* empty (all whitespace) line? */\r
+ if (!*cmd)\r
+ continue;\r
+ \r
+ /* search the end of the current line, ignore trailing whitespace */\r
+ for (p = end = cmd; *p; p++)\r
+ if (!isspace(*p))\r
+ end = p;\r
+ \r
+ /* terminate end */\r
+ *++end = 0;\r
+ if (strcasecmp(cmd, "quit") == 0)\r
+ break;\r
+\r
+ /* run line */\r
+ if ((retval = command_run_line(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION)\r
+ break;\r
+ }\r
+ \r
+ context->mode = old_command_mode;\r
+\r
+ \r
+ free(buffer);\r
+ \r
+ return retval;\r
+}\r
+\r
+void command_print_help_line(command_context_t* context, struct command_s *command, int indent)\r
+{\r
+ command_t *c;\r
+ char indents[32] = {0};\r
+ char *help = "no help available";\r
+ char name_buf[64];\r
+ int i;\r
+ \r
+ for (i = 0; i < indent; i+=2)\r
+ {\r
+ indents[i*2] = ' ';\r
+ indents[i*2+1] = '-';\r
+ }\r
+ indents[i*2] = 0;\r
+ \r
+ if (command->help)\r
+ help = command->help;\r
+ \r
+ snprintf(name_buf, 64, command->name);\r
+ strncat(name_buf, indents, 64);\r
+ command_print(context, "%20s\t%s", name_buf, help);\r
+ \r
+ if (command->children)\r
+ {\r
+ for (c = command->children; c; c = c->next)\r
+ {\r
+ command_print_help_line(context, c, indent + 1);\r
+ }\r
+ }\r
+}\r
+\r
+int command_print_help(command_context_t* context, char* name, char** args, int argc)\r
+{\r
+ command_t *c;\r
+\r
+ for (c = context->commands; c; c = c->next)\r
+ {\r
+ if (argc == 1)\r
+ {\r
+ if (strncasecmp(c->name, args[0], c->unique_len))\r
+ continue;\r
+\r
+ if (strncasecmp(c->name, args[0], strlen(args[0])))\r
+ continue;\r
+ } \r
+\r
+ command_print_help_line(context, c, 0);\r
+ }\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv)\r
+{\r
+ context->output_handler = output_handler;\r
+ context->output_handler_priv = priv;\r
+}\r
+\r
+command_context_t* copy_command_context(command_context_t* context)\r
+{\r
+ command_context_t* copy_context = malloc(sizeof(command_context_t));\r
+\r
+ *copy_context = *context;\r
+ \r
+ return copy_context;\r
+}\r
+\r
+int command_done(command_context_t *context)\r
+{\r
+ free(context);\r
+ context = NULL;\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+command_context_t* command_init()\r
+{\r
+ command_context_t* context = malloc(sizeof(command_context_t));\r
+ \r
+ context->mode = COMMAND_EXEC;\r
+ context->commands = NULL;\r
+ context->current_target = 0;\r
+ context->echo = 0;\r
+ context->output_handler = NULL;\r
+ context->output_handler_priv = NULL;\r
+ \r
+ register_command(context, NULL, "help", command_print_help,\r
+ COMMAND_EXEC, "display this help");\r
+ \r
+ register_command(context, NULL, "sleep", handle_sleep_command,\r
+ COMMAND_ANY, "sleep for <n> milliseconds");\r
+ \r
+ register_command(context, NULL, "time", handle_time_command,\r
+ COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");\r
+ \r
+ return context;\r
+}\r
+\r
+/* sleep command sleeps for <n> miliseconds\r
+ * this is useful in target startup scripts\r
+ */\r
+int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
+{\r
+ unsigned long duration = 0;\r
+ \r
+ if (argc == 1)\r
+ {\r
+ duration = strtoul(args[0], NULL, 0);\r
+ usleep(duration * 1000);\r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
+{\r
+ if (argc<1)\r
+ return ERROR_COMMAND_SYNTAX_ERROR;\r
+ \r
+ duration_t duration;\r
+ char *duration_text;\r
+ int retval;\r
+ \r
+ duration_start_measure(&duration);\r
+ \r
+ retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc, 0);\r
+ \r
+ duration_stop_measure(&duration, &duration_text);\r
+ \r
+ float t=duration.duration.tv_sec;\r
+ t+=((float)duration.duration.tv_usec / 1000000.0);\r
+ command_print(cmd_ctx, "%s took %fs", args[0], t);\r
+ \r
+ free(duration_text);\r
+\r
+ return retval;\r
+}\r
-/***************************************************************************
- * Copyright (C) 2005 by Dominic Rath *
- * Dominic.Rath@gmx.de *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the *
- * Free Software Foundation, Inc., *
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
- ***************************************************************************/
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "replacements.h"
-
-#include "gdb_server.h"
-
-#include "server.h"
-#include "log.h"
-#include "binarybuffer.h"
-#include "jtag.h"
-#include "breakpoints.h"
-#include "flash.h"
-#include "target_request.h"
-#include "configuration.h"
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#if 0
-#define _DEBUG_GDB_IO_
-#endif
-
-static unsigned short gdb_port;
-static const char *DIGITS = "0123456789abcdef";
-
-static void gdb_log_callback(void *priv, const char *file, int line,
- const char *function, const char *format, va_list args);
-
-enum gdb_detach_mode
-{
- GDB_DETACH_RESUME,
- GDB_DETACH_RESET,
- GDB_DETACH_HALT,
- GDB_DETACH_NOTHING
-};
-
-/* target behaviour on gdb detach */
-enum gdb_detach_mode detach_mode = GDB_DETACH_RESUME;
-
-/* set if we are sending a memory map to gdb
- * via qXfer:memory-map:read packet */
-int gdb_use_memory_map = 0;
-int gdb_flash_program = 0;
-
-/* if set, data aborts cause an error to be reported in memory read packets
- * see the code in gdb_read_memory_packet() for further explanations */
-int gdb_report_data_abort = 0;
-
-int gdb_last_signal(target_t *target)
-{
- switch (target->debug_reason)
- {
- case DBG_REASON_DBGRQ:
- return 0x2; /* SIGINT */
- case DBG_REASON_BREAKPOINT:
- case DBG_REASON_WATCHPOINT:
- case DBG_REASON_WPTANDBKPT:
- return 0x05; /* SIGTRAP */
- case DBG_REASON_SINGLESTEP:
- return 0x05; /* SIGTRAP */
- case DBG_REASON_NOTHALTED:
- return 0x0; /* no signal... shouldn't happen */
- default:
- ERROR("BUG: undefined debug reason");
- exit(-1);
- }
-}
-
-int gdb_get_char(connection_t *connection, int* next_char)
-{
- gdb_connection_t *gdb_con = connection->priv;
-
-#ifdef _DEBUG_GDB_IO_
- char *debug_buffer;
-#endif
-
- if (gdb_con->buf_cnt-- > 0)
- {
- *next_char = *(gdb_con->buf_p++);
- if (gdb_con->buf_cnt > 0)
- connection->input_pending = 1;
- else
- connection->input_pending = 0;
-
-#ifdef _DEBUG_GDB_IO_
- DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char);
-#endif
-
- return ERROR_OK;
- }
-
- for (;;)
- {
-#ifndef _WIN32
- /* a non-blocking socket will block if there is 0 bytes available on the socket,
- * but return with as many bytes as are available immediately
- */
- struct timeval tv;
- fd_set read_fds;
-
- FD_ZERO(&read_fds);
- FD_SET(connection->fd, &read_fds);
-
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- if (select(connection->fd + 1, &read_fds, NULL, NULL, &tv) == 0)
- {
- /* This can typically be because a "monitor" command took too long
- * before printing any progress messages
- */
- return ERROR_GDB_TIMEOUT;
- }
-#endif
- gdb_con->buf_cnt = read_socket(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE);
- if (gdb_con->buf_cnt > 0)
- {
- break;
- }
- if (gdb_con->buf_cnt == 0)
- {
- gdb_con->closed = 1;
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
-#ifdef _WIN32
- errno = WSAGetLastError();
-
- switch(errno)
- {
- case WSAEWOULDBLOCK:
- usleep(1000);
- break;
- case WSAECONNABORTED:
- return ERROR_SERVER_REMOTE_CLOSED;
- case WSAECONNRESET:
- return ERROR_SERVER_REMOTE_CLOSED;
- default:
- ERROR("read: %d", errno);
- exit(-1);
- }
-#else
- switch(errno)
- {
- case EAGAIN:
- usleep(1000);
- break;
- case ECONNABORTED:
- return ERROR_SERVER_REMOTE_CLOSED;
- case ECONNRESET:
- return ERROR_SERVER_REMOTE_CLOSED;
- default:
- ERROR("read: %s", strerror(errno));
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-#endif
- }
-
-#ifdef _DEBUG_GDB_IO_
- debug_buffer = malloc(gdb_con->buf_cnt + 1);
- memcpy(debug_buffer, gdb_con->buffer, gdb_con->buf_cnt);
- debug_buffer[gdb_con->buf_cnt] = 0;
- DEBUG("received '%s'", debug_buffer);
- free(debug_buffer);
-#endif
-
- gdb_con->buf_p = gdb_con->buffer;
- gdb_con->buf_cnt--;
- *next_char = *(gdb_con->buf_p++);
- if (gdb_con->buf_cnt > 0)
- connection->input_pending = 1;
- else
- connection->input_pending = 0;
-#ifdef _DEBUG_GDB_IO_
- DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char);
-#endif
-
- return ERROR_OK;
-}
-
-int gdb_putback_char(connection_t *connection, int last_char)
-{
- gdb_connection_t *gdb_con = connection->priv;
-
- if (gdb_con->buf_p > gdb_con->buffer)
- {
- *(--gdb_con->buf_p) = last_char;
- gdb_con->buf_cnt++;
- }
- else
- {
- ERROR("BUG: couldn't put character back");
- }
-
- return ERROR_OK;
-}
-
-/* The only way we can detect that the socket is closed is the first time
- * we write to it, we will fail. Subsequent write operations will
- * succeed. Shudder! */
-int gdb_write(connection_t *connection, void *data, int len)
-{
- gdb_connection_t *gdb_con = connection->priv;
- if (gdb_con->closed)
- return ERROR_SERVER_REMOTE_CLOSED;
-
- if (write_socket(connection->fd, data, len) == len)
- {
- return ERROR_OK;
- }
- gdb_con->closed = 1;
- return ERROR_SERVER_REMOTE_CLOSED;
-}
-
-int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
-{
- int i;
- unsigned char my_checksum = 0;
-#ifdef _DEBUG_GDB_IO_
- char *debug_buffer;
-#endif
- int reply;
- int retval;
- gdb_connection_t *gdb_con = connection->priv;
-
- for (i = 0; i < len; i++)
- my_checksum += buffer[i];
-
- while (1)
- {
-#ifdef _DEBUG_GDB_IO_
- debug_buffer = malloc(len + 1);
- memcpy(debug_buffer, buffer, len);
- debug_buffer[len] = 0;
- DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum);
- free(debug_buffer);
-#endif
-#if 0
- char checksum[3];
- gdb_write(connection, "$", 1);
- if (len > 0)
- gdb_write(connection, buffer, len);
- gdb_write(connection, "#", 1);
-
- snprintf(checksum, 3, "%2.2x", my_checksum);
-
- gdb_write(connection, checksum, 2);
-#else
- void *allocated = NULL;
- char stackAlloc[1024];
- char *t = stackAlloc;
- int totalLen = 1 + len + 1 + 2;
- if (totalLen > sizeof(stackAlloc))
- {
- allocated = malloc(totalLen);
- t = allocated;
- if (allocated == NULL)
- {
- ERROR("Ran out of memory trying to reply packet %d\n", totalLen);
- exit(-1);
- }
- }
- t[0] = '$';
- memcpy(t + 1, buffer, len);
- t[1 + len] = '#';
- t[1 + len + 1] = DIGITS[(my_checksum >> 4) & 0xf];
- t[1 + len + 2] = DIGITS[my_checksum & 0xf];
-
- gdb_write(connection, t, totalLen);
-
- if (allocated)
- {
- free(allocated);
- }
-#endif
- if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)
- return retval;
-
- if (reply == '+')
- break;
- else if (reply == '-')
- {
- /* Stop sending output packets for now */
- log_setCallback(NULL, NULL);
- WARNING("negative reply, retrying");
- }
- else if (reply == 0x3)
- {
- gdb_con->ctrl_c = 1;
- if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)
- return retval;
- if (reply == '+')
- break;
- else if (reply == '-')
- {
- /* Stop sending output packets for now */
- log_setCallback(NULL, NULL);
- WARNING("negative reply, retrying");
- }
- else
- {
- ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);
- return ERROR_SERVER_REMOTE_CLOSED;
- }
- }
- else
- {
- ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);
- return ERROR_SERVER_REMOTE_CLOSED;
- }
- }
- if (gdb_con->closed)
- return ERROR_SERVER_REMOTE_CLOSED;
-
- return ERROR_OK;
-}
-
-int gdb_put_packet(connection_t *connection, char *buffer, int len)
-{
- gdb_connection_t *gdb_con = connection->priv;
- gdb_con->busy = 1;
- int retval = gdb_put_packet_inner(connection, buffer, len);
- gdb_con->busy = 0;
- return retval;
-}
-
-int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)
-{
- int character;
- int count = 0;
- int retval;
- char checksum[3];
- unsigned char my_checksum = 0;
- gdb_connection_t *gdb_con = connection->priv;
-
- while (1)
- {
- do
- {
- if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
- return retval;
-
-#ifdef _DEBUG_GDB_IO_
- DEBUG("character: '%c'", character);
-#endif
-
- switch (character)
- {
- case '$':
- break;
- case '+':
- WARNING("acknowledgment received, but no packet pending");
- break;
- case '-':
- WARNING("negative acknowledgment, but no packet pending");
- break;
- case 0x3:
- gdb_con->ctrl_c = 1;
- *len = 0;
- return ERROR_OK;
- default:
- WARNING("ignoring character 0x%x", character);
- break;
- }
- } while (character != '$');
-
- my_checksum = 0;
-
- count = 0;
- gdb_connection_t *gdb_con = connection->priv;
- for (;;)
- {
- /* The common case is that we have an entire packet with no escape chars.
- * We need to leave at least 2 bytes in the buffer to have
- * gdb_get_char() update various bits and bobs correctly.
- */
- if ((gdb_con->buf_cnt > 2) && ((gdb_con->buf_cnt+count) < *len))
- {
- /* The compiler will struggle a bit with constant propagation and
- * aliasing, so we help it by showing that these values do not
- * change inside the loop
- */
- int i;
- char *buf = gdb_con->buf_p;
- int run = gdb_con->buf_cnt - 2;
- i = 0;
- int done = 0;
- while (i < run)
- {
- character = *buf++;
- i++;
- if (character == '#')
- {
- /* Danger! character can be '#' when esc is
- * used so we need an explicit boolean for done here.
- */
- done = 1;
- break;
- }
-
- if (character == '}')
- {
- /* data transmitted in binary mode (X packet)
- * uses 0x7d as escape character */
- my_checksum += character & 0xff;
- character = *buf++;
- i++;
- my_checksum += character & 0xff;
- buffer[count++] = (character ^ 0x20) & 0xff;
- } else
- {
- my_checksum += character & 0xff;
- buffer[count++] = character & 0xff;
- }
- }
- gdb_con->buf_p += i;
- gdb_con->buf_cnt -= i;
- if (done)
- break;
- }
- if (count > *len)
- {
- ERROR("packet buffer too small");
- return ERROR_GDB_BUFFER_TOO_SMALL;
- }
-
- if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
- return retval;
-
- if (character == '#')
- break;
-
- if (character == '}')
- {
- /* data transmitted in binary mode (X packet)
- * uses 0x7d as escape character */
- my_checksum += character & 0xff;
- if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
- return retval;
- my_checksum += character & 0xff;
- buffer[count++] = (character ^ 0x20) & 0xff;
- }
- else
- {
- my_checksum += character & 0xff;
- buffer[count++] = character & 0xff;
- }
-
- }
-
- *len = count;
-
- if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
- return retval;
- checksum[0] = character;
- if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)
- return retval;
- checksum[1] = character;
- checksum[2] = 0;
-
- if (my_checksum == strtoul(checksum, NULL, 16))
- {
- gdb_write(connection, "+", 1);
- break;
- }
-
- WARNING("checksum error, requesting retransmission");
- gdb_write(connection, "-", 1);
- }
- if (gdb_con->closed)
- return ERROR_SERVER_REMOTE_CLOSED;
-
- return ERROR_OK;
-}
-
-int gdb_get_packet(connection_t *connection, char *buffer, int *len)
-{
- gdb_connection_t *gdb_con = connection->priv;
- gdb_con->busy = 1;
- int retval = gdb_get_packet_inner(connection, buffer, len);
- gdb_con->busy = 0;
- return retval;
-}
-
-int gdb_output_con(connection_t *connection, char* line)
-{
- char *hex_buffer;
- int i, bin_size;
-
- bin_size = strlen(line);
-
- hex_buffer = malloc(bin_size*2 + 4);
-
- hex_buffer[0] = 'O';
- for (i=0; i<bin_size; i++)
- snprintf(hex_buffer + 1 + i*2, 3, "%2.2x", line[i]);
- hex_buffer[bin_size*2+1] = '0';
- hex_buffer[bin_size*2+2] = 'a';
- hex_buffer[bin_size*2+3] = 0x0;
-
- gdb_put_packet(connection, hex_buffer, bin_size*2 + 3);
-
- free(hex_buffer);
- return ERROR_OK;
-}
-
-int gdb_output(struct command_context_s *context, char* line)
-{
- /* this will be dumped to the log and also sent as an O packet if possible */
- USER(line);
- return ERROR_OK;
-}
-
-int gdb_program_handler(struct target_s *target, enum target_event event, void *priv)
-{
- FILE *script;
- struct command_context_s *cmd_ctx = priv;
-
- if (target->gdb_program_script)
- {
- script = open_file_from_path(cmd_ctx, target->gdb_program_script, "r");
- if (!script)
- {
- ERROR("couldn't open script file %s", target->gdb_program_script);
- return ERROR_OK;
- }
-
- INFO("executing gdb_program script '%s'", target->gdb_program_script);
- command_run_file(cmd_ctx, script, COMMAND_EXEC);
- fclose(script);
-
- jtag_execute_queue();
- }
-
- return ERROR_OK;
-}
-
-int gdb_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
-{
- connection_t *connection = priv;
- gdb_connection_t *gdb_connection = connection->priv;
- char sig_reply[4];
- int signal;
-
- switch (event)
- {
- case TARGET_EVENT_HALTED:
- /* In the GDB protocol when we are stepping or coninuing execution,
- * we have a lingering reply. Upon receiving a halted event
- * when we have that lingering packet, we reply to the original
- * step or continue packet.
- *
- * Executing monitor commands can bring the target in and
- * out of the running state so we'll see lots of TARGET_EVENT_XXX
- * that are to be ignored.
- */
- if (gdb_connection->frontend_state == TARGET_RUNNING)
- {
- /* stop forwarding log packets! */
- log_setCallback(NULL, NULL);
-
- if (gdb_connection->ctrl_c)
- {
- signal = 0x2;
- gdb_connection->ctrl_c = 0;
- }
- else
- {
- signal = gdb_last_signal(target);
- }
-
- snprintf(sig_reply, 4, "T%2.2x", signal);
- gdb_put_packet(connection, sig_reply, 3);
- gdb_connection->frontend_state = TARGET_HALTED;
- }
- break;
- case TARGET_EVENT_GDB_PROGRAM:
- gdb_program_handler(target, event, connection->cmd_ctx);
- break;
- default:
- break;
- }
-
- return ERROR_OK;
-}
-
-int gdb_new_connection(connection_t *connection)
-{
- gdb_connection_t *gdb_connection = malloc(sizeof(gdb_connection_t));
- gdb_service_t *gdb_service = connection->service->priv;
- int retval;
- int initial_ack;
-
- connection->priv = gdb_connection;
-
- /* initialize gdb connection information */
- gdb_connection->buf_p = gdb_connection->buffer;
- gdb_connection->buf_cnt = 0;
- gdb_connection->ctrl_c = 0;
- gdb_connection->frontend_state = TARGET_HALTED;
- gdb_connection->vflash_image = NULL;
- gdb_connection->closed = 0;
- gdb_connection->busy = 0;
-
- /* output goes through gdb connection */
- command_set_output_handler(connection->cmd_ctx, gdb_output, connection);
-
- /* register callback to be informed about target events */
- target_register_event_callback(gdb_target_callback_event_handler, connection);
-
- /* a gdb session just attached, put the target in halt mode */
- if (((retval = gdb_service->target->type->halt(gdb_service->target)) != ERROR_OK) &&
- (retval != ERROR_TARGET_ALREADY_HALTED))
- {
- ERROR("error(%d) when trying to halt target, falling back to \"reset halt\"", retval);
- command_run_line(connection->cmd_ctx, "reset halt");
- }
-
- /* This will time out after 1 second */
- command_run_line(connection->cmd_ctx, "wait_halt 1");
-
- /* remove the initial ACK from the incoming buffer */
- if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK)
- return retval;
-
- if (initial_ack != '+')
- gdb_putback_char(connection, initial_ack);
-
- return ERROR_OK;
-}
-
-int gdb_connection_closed(connection_t *connection)
-{
- gdb_service_t *gdb_service = connection->service->priv;
- gdb_connection_t *gdb_connection = connection->priv;
-
- /* see if an image built with vFlash commands is left */
- if (gdb_connection->vflash_image)
- {
- image_close(gdb_connection->vflash_image);
- free(gdb_connection->vflash_image);
- gdb_connection->vflash_image = NULL;
- }
-
- /* if this connection registered a debug-message receiver delete it */
- delete_debug_msg_receiver(connection->cmd_ctx, gdb_service->target);
-
- if (connection->priv)
- {
- free(connection->priv);
- connection->priv = NULL;
- }
- else
- {
- ERROR("BUG: connection->priv == NULL");
- }
-
- target_unregister_event_callback(gdb_target_callback_event_handler, connection);
- log_setCallback(NULL, NULL);
-
- return ERROR_OK;
-}
-
-void gdb_send_error(connection_t *connection, u8 the_error)
-{
- char err[4];
- snprintf(err, 4, "E%2.2X", the_error );
- gdb_put_packet(connection, err, 3);
-}
-
-int gdb_last_signal_packet(connection_t *connection, target_t *target, char* packet, int packet_size)
-{
- char sig_reply[4];
- int signal;
-
- signal = gdb_last_signal(target);
-
- snprintf(sig_reply, 4, "S%2.2x", signal);
- gdb_put_packet(connection, sig_reply, 3);
-
- return ERROR_OK;
-}
-
-/* Convert register to string of bits. NB! The # of bits in the
- * register might be non-divisible by 8(a byte), in which
- * case an entire byte is shown. */
-void gdb_str_to_target(target_t *target, char *tstr, reg_t *reg)
-{
- int i;
-
- u8 *buf;
- int buf_len;
- buf = reg->value;
- buf_len = CEIL(reg->size, 8);
-
- if (target->endianness == TARGET_LITTLE_ENDIAN)
- {
- for (i = 0; i < buf_len; i++)
- {
- tstr[i*2] = DIGITS[(buf[i]>>4) & 0xf];
- tstr[i*2+1] = DIGITS[buf[i]&0xf];
- }
- }
- else
- {
- for (i = 0; i < buf_len; i++)
- {
- tstr[(buf_len-1-i)*2] = DIGITS[(buf[i]>>4)&0xf];
- tstr[(buf_len-1-i)*2+1] = DIGITS[buf[i]&0xf];
- }
- }
-}
-
-void gdb_target_to_str(target_t *target, char *tstr, char *str)
-{
- int str_len = strlen(tstr);
- int i;
-
- if (str_len % 2)
- {
- ERROR("BUG: gdb value with uneven number of characters encountered");
- exit(-1);
- }
-
- if (target->endianness == TARGET_LITTLE_ENDIAN)
- {
- for (i = 0; i < str_len; i+=2)
- {
- str[str_len - i - 1] = tstr[i + 1];
- str[str_len - i - 2] = tstr[i];
- }
- }
- else
- {
- for (i = 0; i < str_len; i++)
- {
- str[i] = tstr[i];
- }
- }
-}
-
-int gdb_get_registers_packet(connection_t *connection, target_t *target, char* packet, int packet_size)
-{
- reg_t **reg_list;
- int reg_list_size;
- int retval;
- int reg_packet_size = 0;
- char *reg_packet;
- char *reg_packet_p;
- int i;
-
-#ifdef _DEBUG_GDB_IO_
- DEBUG("-");
-#endif
-
- if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)
- {
- switch (retval)
- {
- case ERROR_TARGET_NOT_HALTED:
- ERROR("gdb requested registers but we're not halted, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- default:
- /* this is a bug condition - get_gdb_reg_list() may not return any other error */
- ERROR("BUG: unexpected error returned by get_gdb_reg_list()");
- exit(-1);
- }
- }
-
- for (i = 0; i < reg_list_size; i++)
- {
- reg_packet_size += reg_list[i]->size;
- }
-
- reg_packet = malloc(CEIL(reg_packet_size, 8) * 2);
- reg_packet_p = reg_packet;
-
- for (i = 0; i < reg_list_size; i++)
- {
- gdb_str_to_target(target, reg_packet_p, reg_list[i]);
- reg_packet_p += CEIL(reg_list[i]->size, 8) * 2;
- }
-
-#ifdef _DEBUG_GDB_IO_
- {
- char *reg_packet_p;
- reg_packet_p = strndup(reg_packet, CEIL(reg_packet_size, 8) * 2);
- DEBUG("reg_packet: %s", reg_packet_p);
- free(reg_packet_p);
- }
-#endif
-
- gdb_put_packet(connection, reg_packet, CEIL(reg_packet_size, 8) * 2);
- free(reg_packet);
-
- free(reg_list);
-
- return ERROR_OK;
-}
-
-int gdb_set_registers_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
-{
- int i;
- reg_t **reg_list;
- int reg_list_size;
- int retval;
- char *packet_p;
-
-#ifdef _DEBUG_GDB_IO_
- DEBUG("-");
-#endif
-
- /* skip command character */
- packet++;
- packet_size--;
-
- if (packet_size % 2)
- {
- WARNING("GDB set_registers packet with uneven characters received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)
- {
- switch (retval)
- {
- case ERROR_TARGET_NOT_HALTED:
- ERROR("gdb tried to registers but we're not halted, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- default:
- /* this is a bug condition - get_gdb_reg_list() may not return any other error */
- ERROR("BUG: unexpected error returned by get_gdb_reg_list()");
- exit(-1);
- }
- }
-
- packet_p = packet;
- for (i = 0; i < reg_list_size; i++)
- {
- u8 *bin_buf;
- char *hex_buf;
- reg_arch_type_t *arch_type;
-
- /* convert from GDB-string (target-endian) to hex-string (big-endian) */
- hex_buf = malloc(CEIL(reg_list[i]->size, 8) * 2);
- gdb_target_to_str(target, packet_p, hex_buf);
-
- /* convert hex-string to binary buffer */
- bin_buf = malloc(CEIL(reg_list[i]->size, 8));
- str_to_buf(hex_buf, CEIL(reg_list[i]->size, 8) * 2, bin_buf, reg_list[i]->size, 16);
-
- /* get register arch_type, and call set method */
- arch_type = register_get_arch_type(reg_list[i]->arch_type);
- if (arch_type == NULL)
- {
- ERROR("BUG: encountered unregistered arch type");
- exit(-1);
- }
- arch_type->set(reg_list[i], bin_buf);
-
- /* advance packet pointer */
- packet_p += (CEIL(reg_list[i]->size, 8) * 2);
-
- free(bin_buf);
- free(hex_buf);
- }
-
- /* free reg_t *reg_list[] array allocated by get_gdb_reg_list */
- free(reg_list);
-
- gdb_put_packet(connection, "OK", 2);
-
- return ERROR_OK;
-}
-
-int gdb_get_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
-{
- char *reg_packet;
- int reg_num = strtoul(packet + 1, NULL, 16);
- reg_t **reg_list;
- int reg_list_size;
- int retval;
-
-#ifdef _DEBUG_GDB_IO_
- DEBUG("-");
-#endif
-
- if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)
- {
- switch (retval)
- {
- case ERROR_TARGET_NOT_HALTED:
- ERROR("gdb requested registers but we're not halted, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- default:
- /* this is a bug condition - get_gdb_reg_list() may not return any other error */
- ERROR("BUG: unexpected error returned by get_gdb_reg_list()");
- exit(-1);
- }
- }
-
- if (reg_list_size <= reg_num)
- {
- ERROR("gdb requested a non-existing register");
- exit(-1);
- }
-
- reg_packet = malloc(CEIL(reg_list[reg_num]->size, 8) * 2);
-
- gdb_str_to_target(target, reg_packet, reg_list[reg_num]);
-
- gdb_put_packet(connection, reg_packet, CEIL(reg_list[reg_num]->size, 8) * 2);
-
- free(reg_list);
- free(reg_packet);
-
- return ERROR_OK;
-}
-
-int gdb_set_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
-{
- char *separator;
- char *hex_buf;
- u8 *bin_buf;
- int reg_num = strtoul(packet + 1, &separator, 16);
- reg_t **reg_list;
- int reg_list_size;
- int retval;
- reg_arch_type_t *arch_type;
-
- DEBUG("-");
-
- if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)
- {
- switch (retval)
- {
- case ERROR_TARGET_NOT_HALTED:
- ERROR("gdb tried to set a register but we're not halted, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- default:
- /* this is a bug condition - get_gdb_reg_list() may not return any other error */
- ERROR("BUG: unexpected error returned by get_gdb_reg_list()");
- exit(-1);
- }
- }
-
- if (reg_list_size < reg_num)
- {
- ERROR("gdb requested a non-existing register");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- if (*separator != '=')
- {
- ERROR("GDB 'set register packet', but no '=' following the register number");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- /* convert from GDB-string (target-endian) to hex-string (big-endian) */
- hex_buf = malloc(CEIL(reg_list[reg_num]->size, 8) * 2);
- gdb_target_to_str(target, separator + 1, hex_buf);
-
- /* convert hex-string to binary buffer */
- bin_buf = malloc(CEIL(reg_list[reg_num]->size, 8));
- str_to_buf(hex_buf, CEIL(reg_list[reg_num]->size, 8) * 2, bin_buf, reg_list[reg_num]->size, 16);
-
- /* get register arch_type, and call set method */
- arch_type = register_get_arch_type(reg_list[reg_num]->arch_type);
- if (arch_type == NULL)
- {
- ERROR("BUG: encountered unregistered arch type");
- exit(-1);
- }
- arch_type->set(reg_list[reg_num], bin_buf);
-
- gdb_put_packet(connection, "OK", 2);
-
- free(bin_buf);
- free(hex_buf);
- free(reg_list);
-
- return ERROR_OK;
-}
-
-int gdb_memory_packet_error(connection_t *connection, int retval)
-{
- switch (retval)
- {
- case ERROR_TARGET_NOT_HALTED:
- ERROR("gdb tried to read memory but we're not halted, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- case ERROR_TARGET_DATA_ABORT:
- gdb_send_error(connection, EIO);
- break;
- case ERROR_TARGET_TRANSLATION_FAULT:
- gdb_send_error(connection, EFAULT);
- break;
- case ERROR_TARGET_UNALIGNED_ACCESS:
- gdb_send_error(connection, EFAULT);
- break;
- default:
- /* This could be that the target reset itself. */
- ERROR("unexpected error %i. Dropping connection.", retval);
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- return ERROR_OK;
-}
-
-/* We don't have to worry about the default 2 second timeout for GDB packets,
- * because GDB breaks up large memory reads into smaller reads.
- *
- * 8191 bytes by the looks of it. Why 8191 bytes instead of 8192?????
- */
-int gdb_read_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
-{
- char *separator;
- u32 addr = 0;
- u32 len = 0;
-
- u8 *buffer;
- char *hex_buffer;
-
- int retval = ERROR_OK;
-
- /* skip command character */
- packet++;
-
- addr = strtoul(packet, &separator, 16);
-
- if (*separator != ',')
- {
- ERROR("incomplete read memory packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- len = strtoul(separator+1, NULL, 16);
-
- buffer = malloc(len);
-
- DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
-
- retval = target_read_buffer(target, addr, len, buffer);
-
- if ((retval == ERROR_TARGET_DATA_ABORT) && (!gdb_report_data_abort))
- {
- /* TODO : Here we have to lie and send back all zero's lest stack traces won't work.
- * At some point this might be fixed in GDB, in which case this code can be removed.
- *
- * OpenOCD developers are acutely aware of this problem, but there is nothing
- * gained by involving the user in this problem that hopefully will get resolved
- * eventually
- *
- * http://sourceware.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gdb&pr=2395
- *
- * For now, the default is to fix up things to make current GDB versions work.
- * This can be overwritten using the gdb_report_data_abort <'enable'|'disable'> command.
- */
- memset(buffer, 0, len);
- retval = ERROR_OK;
- }
-
- if (retval == ERROR_OK)
- {
- hex_buffer = malloc(len * 2 + 1);
-
- int i;
- for (i = 0; i < len; i++)
- {
- u8 t = buffer[i];
- hex_buffer[2 * i] = DIGITS[(t >> 4) & 0xf];
- hex_buffer[2 * i + 1] = DIGITS[t & 0xf];
- }
-
- gdb_put_packet(connection, hex_buffer, len * 2);
-
- free(hex_buffer);
- }
- else
- {
- retval = gdb_memory_packet_error(connection, retval);
- }
-
- free(buffer);
-
- return retval;
-}
-
-int gdb_write_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
-{
- char *separator;
- u32 addr = 0;
- u32 len = 0;
-
- u8 *buffer;
-
- int i;
- int retval;
-
- /* skip command character */
- packet++;
-
- addr = strtoul(packet, &separator, 16);
-
- if (*separator != ',')
- {
- ERROR("incomplete write memory packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- len = strtoul(separator+1, &separator, 16);
-
- if (*(separator++) != ':')
- {
- ERROR("incomplete write memory packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- buffer = malloc(len);
-
- DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
-
- for (i=0; i<len; i++)
- {
- u32 tmp;
- sscanf(separator + 2*i, "%2x", &tmp);
- buffer[i] = tmp;
- }
-
- retval = target_write_buffer(target, addr, len, buffer);
-
- if (retval == ERROR_OK)
- {
- gdb_put_packet(connection, "OK", 2);
- }
- else
- {
- if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)
- return retval;
- }
-
- free(buffer);
-
- return ERROR_OK;
-}
-
-int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
-{
- char *separator;
- u32 addr = 0;
- u32 len = 0;
-
- int retval;
-
- /* skip command character */
- packet++;
-
- addr = strtoul(packet, &separator, 16);
-
- if (*separator != ',')
- {
- ERROR("incomplete write memory binary packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- len = strtoul(separator+1, &separator, 16);
-
- if (*(separator++) != ':')
- {
- ERROR("incomplete write memory binary packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- retval = ERROR_OK;
- if (len)
- {
- DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);
-
- retval = target_write_buffer(target, addr, len, (u8*)separator);
- }
-
- if (retval == ERROR_OK)
- {
- gdb_put_packet(connection, "OK", 2);
- }
- else
- {
- if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)
- return retval;
- }
-
- return ERROR_OK;
-}
-
-void gdb_step_continue_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
-{
- int current = 0;
- u32 address = 0x0;
-
- DEBUG("-");
-
- if (packet_size > 1)
- {
- packet[packet_size] = 0;
- address = strtoul(packet + 1, NULL, 16);
- }
- else
- {
- current = 1;
- }
-
- if (packet[0] == 'c')
- {
- DEBUG("continue");
- target->type->resume(target, current, address, 0, 0); /* resume at current address, don't handle breakpoints, not debugging */
- }
- else if (packet[0] == 's')
- {
- DEBUG("step");
- target->type->step(target, current, address, 0); /* step at current or address, don't handle breakpoints */
- }
-}
-
-int gdb_bp_wp_packet_error(connection_t *connection, int retval)
-{
- switch (retval)
- {
- case ERROR_TARGET_NOT_HALTED:
- ERROR("gdb tried to set a breakpoint but we're not halted, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- break;
- case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:
- gdb_send_error(connection, EBUSY);
- break;
- default:
- ERROR("BUG: unexpected error %i", retval);
- exit(-1);
- }
-
- return ERROR_OK;
-}
-
-int gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
-{
- int type;
- enum breakpoint_type bp_type = BKPT_SOFT /* dummy init to avoid warning */;
- enum watchpoint_rw wp_type;
- u32 address;
- u32 size;
- char *separator;
- int retval;
-
- DEBUG("-");
-
- type = strtoul(packet + 1, &separator, 16);
-
- if (type == 0) /* memory breakpoint */
- bp_type = BKPT_SOFT;
- else if (type == 1) /* hardware breakpoint */
- bp_type = BKPT_HARD;
- else if (type == 2) /* write watchpoint */
- wp_type = WPT_WRITE;
- else if (type == 3) /* read watchpoint */
- wp_type = WPT_READ;
- else if (type == 4) /* access watchpoint */
- wp_type = WPT_ACCESS;
-
- if (*separator != ',')
- {
- ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- address = strtoul(separator+1, &separator, 16);
-
- if (*separator != ',')
- {
- ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- size = strtoul(separator+1, &separator, 16);
-
- switch (type)
- {
- case 0:
- case 1:
- if (packet[0] == 'Z')
- {
- if ((retval = breakpoint_add(target, address, size, bp_type)) != ERROR_OK)
- {
- if ((retval = gdb_bp_wp_packet_error(connection, retval)) != ERROR_OK)
- return retval;
- }
- else
- {
- gdb_put_packet(connection, "OK", 2);
- }
- }
- else
- {
- breakpoint_remove(target, address);
- gdb_put_packet(connection, "OK", 2);
- }
- break;
- case 2:
- case 3:
- case 4:
- {
- if (packet[0] == 'Z')
- {
- if ((retval = watchpoint_add(target, address, size, type-2, 0, 0xffffffffu)) != ERROR_OK)
- {
- if ((retval = gdb_bp_wp_packet_error(connection, retval)) != ERROR_OK)
- return retval;
- }
- else
- {
- gdb_put_packet(connection, "OK", 2);
- }
- }
- else
- {
- watchpoint_remove(target, address);
- gdb_put_packet(connection, "OK", 2);
- }
- break;
- }
- default:
- break;
- }
-
- return ERROR_OK;
-}
-
-/* print out a string and allocate more space as needed, mainly used for XML at this point */
-void xml_printf(int *retval, char **xml, int *pos, int *size, const char *fmt, ...)
-{
- if (*retval != ERROR_OK)
- {
- return;
- }
- int first = 1;
-
- for (;;)
- {
- if ((*xml == NULL) || (!first))
- {
- /* start by 0 to exercise all the code paths.
- * Need minimum 2 bytes to fit 1 char and 0 terminator. */
-
- *size = *size * 2 + 2;
- char *t = *xml;
- *xml = realloc(*xml, *size);
- if (*xml == NULL)
- {
- if (t)
- free(t);
- *retval = ERROR_SERVER_REMOTE_CLOSED;
- return;
- }
- }
-
- va_list ap;
- int ret;
- va_start(ap, fmt);
- ret = vsnprintf(*xml + *pos, *size - *pos, fmt, ap);
- va_end(ap);
- if ((ret > 0) && ((ret + 1) < *size - *pos))
- {
- *pos += ret;
- return;
- }
- /* there was just enough or not enough space, allocate more. */
- first = 0;
- }
-}
-
-static int decode_xfer_read(char *buf, char **annex, int *ofs, unsigned int *len)
-{
- char *separator;
-
- /* Extract and NUL-terminate the annex. */
- *annex = buf;
- while (*buf && *buf != ':')
- buf++;
- if (*buf == '\0')
- return -1;
- *buf++ = 0;
-
- /* After the read marker and annex, qXfer looks like a
- * traditional 'm' packet. */
-
- *ofs = strtoul(buf, &separator, 16);
-
- if (*separator != ',')
- return -1;
-
- *len = strtoul(separator+1, NULL, 16);
-
- return 0;
-}
-
-int gdb_calc_blocksize(flash_bank_t *bank)
-{
- int i;
- int block_size = 0xffffffff;
-
- /* loop through all sectors and return smallest sector size */
-
- for (i = 0; i < bank->num_sectors; i++)
- {
- if (bank->sectors[i].size < block_size)
- block_size = bank->sectors[i].size;
- }
-
- return block_size;
-}
-
-int gdb_query_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
-{
- command_context_t *cmd_ctx = connection->cmd_ctx;
-
- if (strstr(packet, "qRcmd,"))
- {
- if (packet_size > 6)
- {
- char *cmd;
- int i;
- cmd = malloc((packet_size - 6)/2 + 1);
- for (i=0; i < (packet_size - 6)/2; i++)
- {
- u32 tmp;
- sscanf(packet + 6 + 2*i, "%2x", &tmp);
- cmd[i] = tmp;
- }
- cmd[(packet_size - 6)/2] = 0x0;
-
- /* We want to print all debug output to GDB connection */
- log_setCallback(gdb_log_callback, connection);
- target_call_timer_callbacks();
- command_run_line(cmd_ctx, cmd);
- free(cmd);
- }
- gdb_put_packet(connection, "OK", 2);
- return ERROR_OK;
- }
- else if (strstr(packet, "qCRC:"))
- {
- if (packet_size > 5)
- {
- int retval;
- char gdb_reply[10];
- char *separator;
- u32 checksum;
- u32 addr = 0;
- u32 len = 0;
-
- /* skip command character */
- packet += 5;
-
- addr = strtoul(packet, &separator, 16);
-
- if (*separator != ',')
- {
- ERROR("incomplete read memory packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- len = strtoul(separator + 1, NULL, 16);
-
- retval = target_checksum_memory(target, addr, len, &checksum);
-
- if (retval == ERROR_OK)
- {
- snprintf(gdb_reply, 10, "C%8.8x", checksum);
- gdb_put_packet(connection, gdb_reply, 9);
- }
- else
- {
- if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)
- return retval;
- }
-
- return ERROR_OK;
- }
- }
- else if (strstr(packet, "qSupported"))
- {
- /* we currently support packet size and qXfer:memory-map:read (if enabled)
- * disable qXfer:features:read for the moment */
- int retval = ERROR_OK;
- char *buffer = NULL;
- int pos = 0;
- int size = 0;
-
- xml_printf(&retval, &buffer, &pos, &size,
- "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read-",
- (GDB_BUFFER_SIZE - 1), gdb_use_memory_map == 1 ? '+' : '-');
-
- if (retval != ERROR_OK)
- {
- gdb_send_error(connection, 01);
- return ERROR_OK;
- }
-
- gdb_put_packet(connection, buffer, strlen(buffer));
- free(buffer);
-
- return ERROR_OK;
- }
- else if (strstr(packet, "qXfer:memory-map:read::"))
- {
- /* We get away with only specifying flash here. Regions that are not
- * specified are treated as if we provided no memory map(if not we
- * could detect the holes and mark them as RAM).
- * Normally we only execute this code once, but no big deal if we
- * have to regenerate it a couple of times. */
-
- flash_bank_t *p;
- char *xml = NULL;
- int size = 0;
- int pos = 0;
- int retval = ERROR_OK;
-
- int offset;
- int length;
- char *separator;
- int blocksize;
-
- /* skip command character */
- packet += 23;
-
- offset = strtoul(packet, &separator, 16);
- length = strtoul(separator + 1, &separator, 16);
-
- xml_printf(&retval, &xml, &pos, &size, "<memory-map>\n");
-
- int i = 0;
- for (;;)
- {
- p = get_flash_bank_by_num(i);
- if (p == NULL)
- break;
-
- /* if device has uneven sector sizes, eg. str7, lpc
- * we pass the smallest sector size to gdb memory map */
- blocksize = gdb_calc_blocksize(p);
-
- xml_printf(&retval, &xml, &pos, &size, "<memory type=\"flash\" start=\"0x%x\" length=\"0x%x\">\n" \
- "<property name=\"blocksize\">0x%x</property>\n" \
- "</memory>\n", \
- p->base, p->size, blocksize);
- i++;
- }
-
- xml_printf(&retval, &xml, &pos, &size, "</memory-map>\n");
-
- if (retval != ERROR_OK)
- {
- gdb_send_error(connection, retval);
- return retval;
- }
-
- if (offset + length > pos)
- {
- length = pos - offset;
- }
-
- char *t = malloc(length + 1);
- t[0] = 'l';
- memcpy(t + 1, xml + offset, length);
- gdb_put_packet(connection, t, length + 1);
-
- free(t);
- free(xml);
- return ERROR_OK;
- }
- else if (strstr(packet, "qXfer:features:read:"))
- {
- char *xml = NULL;
- int size = 0;
- int pos = 0;
- int retval = ERROR_OK;
-
- int offset;
- unsigned int length;
- char *annex;
-
- /* skip command character */
- packet += 20;
-
- if (decode_xfer_read(packet, &annex, &offset, &length) < 0)
- {
- gdb_send_error(connection, 01);
- return ERROR_OK;
- }
-
- if (strcmp(annex, "target.xml") != 0)
- {
- gdb_send_error(connection, 01);
- return ERROR_OK;
- }
-
- xml_printf(&retval, &xml, &pos, &size, \
- "l<target version=\"1.0\">\n<architecture>arm</architecture>\n</target>\n");
-
- if (retval != ERROR_OK)
- {
- gdb_send_error(connection, retval);
- return retval;
- }
-
- gdb_put_packet(connection, xml, strlen(xml) + 1);
-
- free(xml);
- return ERROR_OK;
- }
-
- gdb_put_packet(connection, "", 0);
- return ERROR_OK;
-}
-
-int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
-{
- gdb_connection_t *gdb_connection = connection->priv;
- gdb_service_t *gdb_service = connection->service->priv;
- int result;
-
- /* if flash programming disabled - send a empty reply */
-
- if (gdb_flash_program == 0)
- {
- gdb_put_packet(connection, "", 0);
- return ERROR_OK;
- }
-
- if (strstr(packet, "vFlashErase:"))
- {
- unsigned long addr;
- unsigned long length;
-
- char *parse = packet + 12;
- if (*parse == '\0')
- {
- ERROR("incomplete vFlashErase packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- addr = strtoul(parse, &parse, 16);
-
- if (*(parse++) != ',' || *parse == '\0')
- {
- ERROR("incomplete vFlashErase packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- length = strtoul(parse, &parse, 16);
-
- if (*parse != '\0')
- {
- ERROR("incomplete vFlashErase packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- /* assume all sectors need erasing - stops any problems
- * when flash_write is called multiple times */
- flash_set_dirty();
-
- /* perform any target specific operations before the erase */
- target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_PROGRAM);
-
- /* perform erase */
- if ((result = flash_erase_address_range(gdb_service->target, addr, length)) != ERROR_OK)
- {
- /* GDB doesn't evaluate the actual error number returned,
- * treat a failed erase as an I/O error
- */
- gdb_send_error(connection, EIO);
- ERROR("flash_erase returned %i", result);
- }
- else
- gdb_put_packet(connection, "OK", 2);
-
- return ERROR_OK;
- }
-
- if (strstr(packet, "vFlashWrite:"))
- {
- unsigned long addr;
- unsigned long length;
- char *parse = packet + 12;
-
- if (*parse == '\0')
- {
- ERROR("incomplete vFlashErase packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
- addr = strtoul(parse, &parse, 16);
- if (*(parse++) != ':')
- {
- ERROR("incomplete vFlashErase packet received, dropping connection");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
- length = packet_size - (parse - packet);
-
- /* create a new image if there isn't already one */
- if (gdb_connection->vflash_image == NULL)
- {
- gdb_connection->vflash_image = malloc(sizeof(image_t));
- image_open(gdb_connection->vflash_image, "", "build");
- }
-
- /* create new section with content from packet buffer */
- image_add_section(gdb_connection->vflash_image, addr, length, 0x0, (u8*)parse);
-
- gdb_put_packet(connection, "OK", 2);
-
- return ERROR_OK;
- }
-
- if (!strcmp(packet, "vFlashDone"))
- {
- u32 written;
-
- /* process the flashing buffer. No need to erase as GDB
- * always issues a vFlashErase first. */
- if ((result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, 0)) != ERROR_OK)
- {
- if (result == ERROR_FLASH_DST_OUT_OF_BANK)
- gdb_put_packet(connection, "E.memtype", 9);
- else
- gdb_send_error(connection, EIO);
- }
- else
- {
- DEBUG("wrote %u bytes from vFlash image to flash", written);
- gdb_put_packet(connection, "OK", 2);
- }
-
- image_close(gdb_connection->vflash_image);
- free(gdb_connection->vflash_image);
- gdb_connection->vflash_image = NULL;
-
- return ERROR_OK;
- }
-
- gdb_put_packet(connection, "", 0);
- return ERROR_OK;
-}
-
-int gdb_detach(connection_t *connection, target_t *target)
-{
- switch( detach_mode )
- {
- case GDB_DETACH_RESUME:
- target->type->resume(target, 1, 0, 1, 0);
- break;
-
- case GDB_DETACH_RESET:
- target_process_reset(connection->cmd_ctx);
- break;
-
- case GDB_DETACH_HALT:
- target->type->halt(target);
- break;
-
- case GDB_DETACH_NOTHING:
- break;
- }
-
- gdb_put_packet(connection, "OK", 2);
-
- return ERROR_OK;
-}
-
-static void gdb_log_callback(void *priv, const char *file, int line,
- const char *function, const char *format, va_list args)
-{
- connection_t *connection = priv;
- gdb_connection_t *gdb_con = connection->priv;
-
- if (gdb_con->busy)
- {
- /* do not reply this using the O packet */
- return;
- }
-
- char *t = allocPrintf(format, args);
- if (t == NULL)
- return;
-
- gdb_output_con(connection, t);
-
- free(t);
-}
-
-int gdb_input_inner(connection_t *connection)
-{
- gdb_service_t *gdb_service = connection->service->priv;
- target_t *target = gdb_service->target;
- char packet[GDB_BUFFER_SIZE];
- int packet_size;
- int retval;
- gdb_connection_t *gdb_con = connection->priv;
- static int extended_protocol = 0;
-
- /* drain input buffer */
- do
- {
- packet_size = GDB_BUFFER_SIZE-1;
- if ((retval = gdb_get_packet(connection, packet, &packet_size)) != ERROR_OK)
- {
- return retval;
- }
-
- /* terminate with zero */
- packet[packet_size] = 0;
-
- DEBUG("received packet: '%s'", packet);
-
- if (packet_size > 0)
- {
- retval = ERROR_OK;
- switch (packet[0])
- {
- case 'H':
- /* Hct... -- set thread
- * we don't have threads, send empty reply */
- gdb_put_packet(connection, NULL, 0);
- break;
- case 'q':
- retval = gdb_query_packet(connection, target, packet, packet_size);
- break;
- case 'g':
- retval = gdb_get_registers_packet(connection, target, packet, packet_size);
- break;
- case 'G':
- retval = gdb_set_registers_packet(connection, target, packet, packet_size);
- break;
- case 'p':
- retval = gdb_get_register_packet(connection, target, packet, packet_size);
- break;
- case 'P':
- retval = gdb_set_register_packet(connection, target, packet, packet_size);
- break;
- case 'm':
- retval = gdb_read_memory_packet(connection, target, packet, packet_size);
- break;
- case 'M':
- retval = gdb_write_memory_packet(connection, target, packet, packet_size);
- break;
- case 'z':
- case 'Z':
- retval = gdb_breakpoint_watchpoint_packet(connection, target, packet, packet_size);
- break;
- case '?':
- gdb_last_signal_packet(connection, target, packet, packet_size);
- break;
- case 'c':
- case 's':
- {
- /* We're running/stepping, in which case we can
- * forward log output until the target is halted */
- gdb_connection_t *gdb_con = connection->priv;
- gdb_con->frontend_state = TARGET_RUNNING;
- log_setCallback(gdb_log_callback, connection);
- gdb_step_continue_packet(connection, target, packet, packet_size);
- }
- break;
- case 'v':
- retval = gdb_v_packet(connection, target, packet, packet_size);
- break;
- case 'D':
- retval = gdb_detach(connection, target);
- extended_protocol = 0;
- break;
- case 'X':
- if ((retval = gdb_write_memory_binary_packet(connection, target, packet, packet_size)) != ERROR_OK)
- return retval;
- break;
- case 'k':
- if (extended_protocol != 0)
- break;
- gdb_put_packet(connection, "OK", 2);
- return ERROR_SERVER_REMOTE_CLOSED;
- case '!':
- /* handle extended remote protocol */
- extended_protocol = 1;
- gdb_put_packet(connection, "OK", 2);
- break;
- case 'R':
- /* handle extended restart packet */
- target_process_reset(connection->cmd_ctx);
- break;
- default:
- /* ignore unkown packets */
- DEBUG("ignoring 0x%2.2x packet", packet[0]);
- gdb_put_packet(connection, NULL, 0);
- break;
- }
-
- /* if a packet handler returned an error, exit input loop */
- if (retval != ERROR_OK)
- return retval;
- }
-
- if (gdb_con->ctrl_c)
- {
- if (target->state == TARGET_RUNNING)
- {
- target->type->halt(target);
- gdb_con->ctrl_c = 0;
- }
- }
-
- } while (gdb_con->buf_cnt > 0);
-
- return ERROR_OK;
-}
-
-int gdb_input(connection_t *connection)
-{
- int retval = gdb_input_inner(connection);
- if (retval == ERROR_SERVER_REMOTE_CLOSED)
- return retval;
- /* we'll recover from any other errors(e.g. temporary timeouts, etc.) */
- return ERROR_OK;
-}
-
-int gdb_init()
-{
- gdb_service_t *gdb_service;
- target_t *target = targets;
- int i = 0;
-
- if (!target)
- {
- WARNING("no gdb ports allocated as no target has been specified");
- return ERROR_OK;
- }
-
- if (gdb_port == 0)
- {
- WARNING("no gdb port specified, using default port 3333");
- gdb_port = 3333;
- }
-
- while (target)
- {
- char service_name[8];
-
- snprintf(service_name, 8, "gdb-%2.2i", i);
-
- gdb_service = malloc(sizeof(gdb_service_t));
- gdb_service->target = target;
-
- add_service("gdb", CONNECTION_GDB, gdb_port + i, 1, gdb_new_connection, gdb_input, gdb_connection_closed, gdb_service);
-
- DEBUG("gdb service for target %s at port %i", target->type->name, gdb_port + i);
-
- i++;
- target = target->next;
- }
-
- return ERROR_OK;
-}
-
-/* daemon configuration command gdb_port */
-int handle_gdb_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- if (argc == 0)
- return ERROR_OK;
-
- /* only if the port wasn't overwritten by cmdline */
- if (gdb_port == 0)
- gdb_port = strtoul(args[0], NULL, 0);
-
- return ERROR_OK;
-}
-
-int handle_gdb_detach_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- if (argc == 1)
- {
- if (strcmp(args[0], "resume") == 0)
- {
- detach_mode = GDB_DETACH_RESUME;
- return ERROR_OK;
- }
- else if (strcmp(args[0], "reset") == 0)
- {
- detach_mode = GDB_DETACH_RESET;
- return ERROR_OK;
- }
- else if (strcmp(args[0], "halt") == 0)
- {
- detach_mode = GDB_DETACH_HALT;
- return ERROR_OK;
- }
- else if (strcmp(args[0], "nothing") == 0)
- {
- detach_mode = GDB_DETACH_NOTHING;
- return ERROR_OK;
- }
- }
-
- WARNING("invalid gdb_detach configuration directive: %s", args[0]);
- return ERROR_OK;
-}
-
-int handle_gdb_memory_map_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- if (argc == 1)
- {
- if (strcmp(args[0], "enable") == 0)
- {
- gdb_use_memory_map = 1;
- return ERROR_OK;
- }
- else if (strcmp(args[0], "disable") == 0)
- {
- gdb_use_memory_map = 0;
- return ERROR_OK;
- }
- }
-
- WARNING("invalid gdb_memory_map configuration directive: %s", args[0]);
- return ERROR_OK;
-}
-
-int handle_gdb_flash_program_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- if (argc == 1)
- {
- if (strcmp(args[0], "enable") == 0)
- {
- gdb_flash_program = 1;
- return ERROR_OK;
- }
- else if (strcmp(args[0], "disable") == 0)
- {
- gdb_flash_program = 0;
- return ERROR_OK;
- }
- }
-
- WARNING("invalid gdb_memory_map configuration directive: %s", args[0]);
- return ERROR_OK;
-}
-
-int handle_gdb_report_data_abort_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- if (argc == 1)
- {
- if (strcmp(args[0], "enable") == 0)
- {
- gdb_report_data_abort = 1;
- return ERROR_OK;
- }
- else if (strcmp(args[0], "disable") == 0)
- {
- gdb_report_data_abort = 0;
- return ERROR_OK;
- }
- }
-
- WARNING("invalid gdb_report_data_abort configuration directive: %s", args[0]);
- return ERROR_OK;
-}
-
-int gdb_register_commands(command_context_t *command_context)
-{
- register_command(command_context, NULL, "gdb_port", handle_gdb_port_command,
- COMMAND_CONFIG, "");
- register_command(command_context, NULL, "gdb_detach", handle_gdb_detach_command,
- COMMAND_CONFIG, "");
- register_command(command_context, NULL, "gdb_memory_map", handle_gdb_memory_map_command,
- COMMAND_CONFIG, "");
- register_command(command_context, NULL, "gdb_flash_program", handle_gdb_flash_program_command,
- COMMAND_CONFIG, "");
- register_command(command_context, NULL, "gdb_report_data_abort", handle_gdb_report_data_abort_command,
- COMMAND_CONFIG, "");
- return ERROR_OK;
-}
+/***************************************************************************\r
+ * Copyright (C) 2005 by Dominic Rath *\r
+ * Dominic.Rath@gmx.de *\r
+ * *\r
+ * This program is free software; you can redistribute it and/or modify *\r
+ * it under the terms of the GNU General Public License as published by *\r
+ * the Free Software Foundation; either version 2 of the License, or *\r
+ * (at your option) any later version. *\r
+ * *\r
+ * This program is distributed in the hope that it will be useful, *\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *\r
+ * GNU General Public License for more details. *\r
+ * *\r
+ * You should have received a copy of the GNU General Public License *\r
+ * along with this program; if not, write to the *\r
+ * Free Software Foundation, Inc., *\r
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *\r
+ ***************************************************************************/\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#include "replacements.h"\r
+\r
+#include "gdb_server.h"\r
+\r
+#include "server.h"\r
+#include "log.h"\r
+#include "binarybuffer.h"\r
+#include "jtag.h"\r
+#include "breakpoints.h"\r
+#include "flash.h"\r
+#include "target_request.h"\r
+#include "configuration.h"\r
+\r
+#include <string.h>\r
+#include <errno.h>\r
+#include <unistd.h>\r
+#include <stdlib.h>\r
+\r
+#if 0\r
+#define _DEBUG_GDB_IO_\r
+#endif\r
+\r
+static unsigned short gdb_port;\r
+static const char *DIGITS = "0123456789abcdef";\r
+\r
+static void gdb_log_callback(void *priv, const char *file, int line, \r
+ const char *function, const char *format, va_list args);\r
+\r
+enum gdb_detach_mode\r
+{\r
+ GDB_DETACH_RESUME,\r
+ GDB_DETACH_RESET,\r
+ GDB_DETACH_HALT,\r
+ GDB_DETACH_NOTHING\r
+};\r
+\r
+/* target behaviour on gdb detach */\r
+enum gdb_detach_mode detach_mode = GDB_DETACH_RESUME;\r
+\r
+/* set if we are sending a memory map to gdb\r
+ * via qXfer:memory-map:read packet */\r
+int gdb_use_memory_map = 0;\r
+int gdb_flash_program = 0;\r
+\r
+/* if set, data aborts cause an error to be reported in memory read packets\r
+ * see the code in gdb_read_memory_packet() for further explanations */\r
+int gdb_report_data_abort = 0;\r
+\r
+int gdb_last_signal(target_t *target)\r
+{\r
+ switch (target->debug_reason)\r
+ {\r
+ case DBG_REASON_DBGRQ:\r
+ return 0x2; /* SIGINT */\r
+ case DBG_REASON_BREAKPOINT:\r
+ case DBG_REASON_WATCHPOINT:\r
+ case DBG_REASON_WPTANDBKPT:\r
+ return 0x05; /* SIGTRAP */\r
+ case DBG_REASON_SINGLESTEP:\r
+ return 0x05; /* SIGTRAP */\r
+ case DBG_REASON_NOTHALTED:\r
+ return 0x0; /* no signal... shouldn't happen */\r
+ default:\r
+ ERROR("BUG: undefined debug reason");\r
+ exit(-1);\r
+ }\r
+}\r
+\r
+int gdb_get_char(connection_t *connection, int* next_char)\r
+{\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+\r
+#ifdef _DEBUG_GDB_IO_\r
+ char *debug_buffer;\r
+#endif\r
+\r
+ if (gdb_con->buf_cnt-- > 0)\r
+ {\r
+ *next_char = *(gdb_con->buf_p++);\r
+ if (gdb_con->buf_cnt > 0)\r
+ connection->input_pending = 1;\r
+ else\r
+ connection->input_pending = 0;\r
+\r
+#ifdef _DEBUG_GDB_IO_\r
+ DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char);\r
+#endif\r
+\r
+ return ERROR_OK;\r
+ }\r
+\r
+ for (;;)\r
+ {\r
+#ifndef _WIN32\r
+ /* a non-blocking socket will block if there is 0 bytes available on the socket,\r
+ * but return with as many bytes as are available immediately\r
+ */\r
+ struct timeval tv;\r
+ fd_set read_fds;\r
+ \r
+ FD_ZERO(&read_fds);\r
+ FD_SET(connection->fd, &read_fds);\r
+ \r
+ tv.tv_sec = 1;\r
+ tv.tv_usec = 0;\r
+ if (select(connection->fd + 1, &read_fds, NULL, NULL, &tv) == 0)\r
+ {\r
+ /* This can typically be because a "monitor" command took too long\r
+ * before printing any progress messages\r
+ */\r
+ return ERROR_GDB_TIMEOUT; \r
+ }\r
+#endif\r
+ gdb_con->buf_cnt = read_socket(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE);\r
+ if (gdb_con->buf_cnt > 0)\r
+ {\r
+ break;\r
+ }\r
+ if (gdb_con->buf_cnt == 0)\r
+ {\r
+ gdb_con->closed = 1;\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+#ifdef _WIN32\r
+ errno = WSAGetLastError();\r
+\r
+ switch(errno)\r
+ {\r
+ case WSAEWOULDBLOCK:\r
+ usleep(1000);\r
+ break;\r
+ case WSAECONNABORTED:\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ case WSAECONNRESET:\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ default:\r
+ ERROR("read: %d", errno);\r
+ exit(-1);\r
+ }\r
+#else\r
+ switch(errno)\r
+ {\r
+ case EAGAIN:\r
+ usleep(1000);\r
+ break;\r
+ case ECONNABORTED:\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ case ECONNRESET:\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ default:\r
+ ERROR("read: %s", strerror(errno));\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+#endif\r
+ }\r
+\r
+#ifdef _DEBUG_GDB_IO_\r
+ debug_buffer = malloc(gdb_con->buf_cnt + 1);\r
+ memcpy(debug_buffer, gdb_con->buffer, gdb_con->buf_cnt);\r
+ debug_buffer[gdb_con->buf_cnt] = 0;\r
+ DEBUG("received '%s'", debug_buffer);\r
+ free(debug_buffer);\r
+#endif\r
+\r
+ gdb_con->buf_p = gdb_con->buffer;\r
+ gdb_con->buf_cnt--;\r
+ *next_char = *(gdb_con->buf_p++);\r
+ if (gdb_con->buf_cnt > 0)\r
+ connection->input_pending = 1;\r
+ else\r
+ connection->input_pending = 0; \r
+#ifdef _DEBUG_GDB_IO_\r
+ DEBUG("returned char '%c' (0x%2.2x)", *next_char, *next_char);\r
+#endif\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_putback_char(connection_t *connection, int last_char)\r
+{\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+\r
+ if (gdb_con->buf_p > gdb_con->buffer)\r
+ {\r
+ *(--gdb_con->buf_p) = last_char;\r
+ gdb_con->buf_cnt++;\r
+ }\r
+ else\r
+ {\r
+ ERROR("BUG: couldn't put character back"); \r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+/* The only way we can detect that the socket is closed is the first time\r
+ * we write to it, we will fail. Subsequent write operations will\r
+ * succeed. Shudder! */\r
+int gdb_write(connection_t *connection, void *data, int len)\r
+{\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+ if (gdb_con->closed)\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+\r
+ if (write_socket(connection->fd, data, len) == len)\r
+ {\r
+ return ERROR_OK;\r
+ }\r
+ gdb_con->closed = 1;\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+}\r
+\r
+int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)\r
+{\r
+ int i;\r
+ unsigned char my_checksum = 0;\r
+#ifdef _DEBUG_GDB_IO_\r
+ char *debug_buffer;\r
+#endif\r
+ int reply;\r
+ int retval;\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+\r
+ for (i = 0; i < len; i++)\r
+ my_checksum += buffer[i];\r
+\r
+ while (1)\r
+ {\r
+#ifdef _DEBUG_GDB_IO_\r
+ debug_buffer = malloc(len + 1);\r
+ memcpy(debug_buffer, buffer, len);\r
+ debug_buffer[len] = 0;\r
+ DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum);\r
+ free(debug_buffer);\r
+#endif\r
+#if 0\r
+ char checksum[3];\r
+ gdb_write(connection, "$", 1);\r
+ if (len > 0)\r
+ gdb_write(connection, buffer, len);\r
+ gdb_write(connection, "#", 1);\r
+ \r
+ snprintf(checksum, 3, "%2.2x", my_checksum);\r
+ \r
+ gdb_write(connection, checksum, 2);\r
+#else\r
+ void *allocated = NULL;\r
+ char stackAlloc[1024];\r
+ char *t = stackAlloc;\r
+ int totalLen = 1 + len + 1 + 2;\r
+ if (totalLen > sizeof(stackAlloc))\r
+ {\r
+ allocated = malloc(totalLen);\r
+ t = allocated;\r
+ if (allocated == NULL)\r
+ {\r
+ ERROR("Ran out of memory trying to reply packet %d\n", totalLen);\r
+ exit(-1);\r
+ }\r
+ }\r
+ t[0] = '$';\r
+ memcpy(t + 1, buffer, len);\r
+ t[1 + len] = '#';\r
+ t[1 + len + 1] = DIGITS[(my_checksum >> 4) & 0xf];\r
+ t[1 + len + 2] = DIGITS[my_checksum & 0xf];\r
+ \r
+ gdb_write(connection, t, totalLen);\r
+ \r
+ if (allocated)\r
+ {\r
+ free(allocated);\r
+ }\r
+#endif\r
+ if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)\r
+ return retval;\r
+\r
+ if (reply == '+')\r
+ break;\r
+ else if (reply == '-')\r
+ {\r
+ /* Stop sending output packets for now */\r
+ log_remove_callback(gdb_log_callback, connection);\r
+ WARNING("negative reply, retrying");\r
+ }\r
+ else if (reply == 0x3)\r
+ {\r
+ gdb_con->ctrl_c = 1;\r
+ if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)\r
+ return retval;\r
+ if (reply == '+')\r
+ break;\r
+ else if (reply == '-')\r
+ {\r
+ /* Stop sending output packets for now */\r
+ log_remove_callback(gdb_log_callback, connection);\r
+ WARNING("negative reply, retrying");\r
+ }\r
+ else\r
+ {\r
+ ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+ }\r
+ if (gdb_con->closed)\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_put_packet(connection_t *connection, char *buffer, int len)\r
+{\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+ gdb_con->busy = 1;\r
+ int retval = gdb_put_packet_inner(connection, buffer, len);\r
+ gdb_con->busy = 0;\r
+ return retval;\r
+}\r
+\r
+int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)\r
+{\r
+ int character;\r
+ int count = 0;\r
+ int retval;\r
+ char checksum[3];\r
+ unsigned char my_checksum = 0;\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+\r
+ while (1)\r
+ {\r
+ do\r
+ {\r
+ if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)\r
+ return retval;\r
+\r
+#ifdef _DEBUG_GDB_IO_\r
+ DEBUG("character: '%c'", character);\r
+#endif\r
+\r
+ switch (character)\r
+ {\r
+ case '$':\r
+ break;\r
+ case '+':\r
+ WARNING("acknowledgment received, but no packet pending");\r
+ break;\r
+ case '-':\r
+ WARNING("negative acknowledgment, but no packet pending");\r
+ break;\r
+ case 0x3:\r
+ gdb_con->ctrl_c = 1;\r
+ *len = 0;\r
+ return ERROR_OK;\r
+ default:\r
+ WARNING("ignoring character 0x%x", character);\r
+ break;\r
+ }\r
+ } while (character != '$');\r
+\r
+ my_checksum = 0;\r
+ \r
+ count = 0;\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+ for (;;)\r
+ {\r
+ /* The common case is that we have an entire packet with no escape chars.\r
+ * We need to leave at least 2 bytes in the buffer to have\r
+ * gdb_get_char() update various bits and bobs correctly. \r
+ */\r
+ if ((gdb_con->buf_cnt > 2) && ((gdb_con->buf_cnt+count) < *len))\r
+ {\r
+ /* The compiler will struggle a bit with constant propagation and\r
+ * aliasing, so we help it by showing that these values do not\r
+ * change inside the loop \r
+ */ \r
+ int i;\r
+ char *buf = gdb_con->buf_p;\r
+ int run = gdb_con->buf_cnt - 2;\r
+ i = 0;\r
+ int done = 0;\r
+ while (i < run)\r
+ {\r
+ character = *buf++;\r
+ i++;\r
+ if (character == '#')\r
+ {\r
+ /* Danger! character can be '#' when esc is \r
+ * used so we need an explicit boolean for done here.\r
+ */\r
+ done = 1;\r
+ break;\r
+ }\r
+ \r
+ if (character == '}')\r
+ {\r
+ /* data transmitted in binary mode (X packet)\r
+ * uses 0x7d as escape character */\r
+ my_checksum += character & 0xff;\r
+ character = *buf++;\r
+ i++;\r
+ my_checksum += character & 0xff;\r
+ buffer[count++] = (character ^ 0x20) & 0xff;\r
+ } else\r
+ {\r
+ my_checksum += character & 0xff;\r
+ buffer[count++] = character & 0xff;\r
+ }\r
+ }\r
+ gdb_con->buf_p += i;\r
+ gdb_con->buf_cnt -= i;\r
+ if (done) \r
+ break;\r
+ } \r
+ if (count > *len)\r
+ {\r
+ ERROR("packet buffer too small");\r
+ return ERROR_GDB_BUFFER_TOO_SMALL;\r
+ }\r
+ \r
+ if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)\r
+ return retval;\r
+\r
+ if (character == '#')\r
+ break;\r
+\r
+ if (character == '}')\r
+ {\r
+ /* data transmitted in binary mode (X packet)\r
+ * uses 0x7d as escape character */\r
+ my_checksum += character & 0xff;\r
+ if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)\r
+ return retval;\r
+ my_checksum += character & 0xff;\r
+ buffer[count++] = (character ^ 0x20) & 0xff;\r
+ }\r
+ else\r
+ {\r
+ my_checksum += character & 0xff;\r
+ buffer[count++] = character & 0xff;\r
+ }\r
+\r
+ }\r
+\r
+ *len = count;\r
+\r
+ if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)\r
+ return retval;\r
+ checksum[0] = character;\r
+ if ((retval = gdb_get_char(connection, &character)) != ERROR_OK)\r
+ return retval;\r
+ checksum[1] = character;\r
+ checksum[2] = 0;\r
+\r
+ if (my_checksum == strtoul(checksum, NULL, 16))\r
+ {\r
+ gdb_write(connection, "+", 1);\r
+ break;\r
+ }\r
+\r
+ WARNING("checksum error, requesting retransmission");\r
+ gdb_write(connection, "-", 1);\r
+ }\r
+ if (gdb_con->closed)\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_get_packet(connection_t *connection, char *buffer, int *len)\r
+{\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+ gdb_con->busy = 1;\r
+ int retval = gdb_get_packet_inner(connection, buffer, len);\r
+ gdb_con->busy = 0;\r
+ return retval;\r
+}\r
+ \r
+int gdb_output_con(connection_t *connection, char* line)\r
+{\r
+ char *hex_buffer;\r
+ int i, bin_size;\r
+\r
+ bin_size = strlen(line);\r
+\r
+ hex_buffer = malloc(bin_size*2 + 4);\r
+\r
+ hex_buffer[0] = 'O';\r
+ for (i=0; i<bin_size; i++)\r
+ snprintf(hex_buffer + 1 + i*2, 3, "%2.2x", line[i]);\r
+ hex_buffer[bin_size*2+1] = '0';\r
+ hex_buffer[bin_size*2+2] = 'a';\r
+ hex_buffer[bin_size*2+3] = 0x0;\r
+\r
+ gdb_put_packet(connection, hex_buffer, bin_size*2 + 3);\r
+\r
+ free(hex_buffer);\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_output(struct command_context_s *context, char* line)\r
+{\r
+ /* this will be dumped to the log and also sent as an O packet if possible */\r
+ USER(line); \r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_program_handler(struct target_s *target, enum target_event event, void *priv)\r
+{\r
+ FILE *script;\r
+ struct command_context_s *cmd_ctx = priv;\r
+ \r
+ if (target->gdb_program_script)\r
+ {\r
+ script = open_file_from_path(cmd_ctx, target->gdb_program_script, "r");\r
+ if (!script)\r
+ {\r
+ ERROR("couldn't open script file %s", target->gdb_program_script);\r
+ return ERROR_OK;\r
+ }\r
+\r
+ INFO("executing gdb_program script '%s'", target->gdb_program_script);\r
+ command_run_file(cmd_ctx, script, COMMAND_EXEC);\r
+ fclose(script);\r
+ \r
+ jtag_execute_queue();\r
+ }\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)\r
+{\r
+ connection_t *connection = priv;\r
+ gdb_connection_t *gdb_connection = connection->priv;\r
+ char sig_reply[4];\r
+ int signal;\r
+\r
+ switch (event)\r
+ {\r
+ case TARGET_EVENT_HALTED:\r
+ /* In the GDB protocol when we are stepping or coninuing execution,\r
+ * we have a lingering reply. Upon receiving a halted event \r
+ * when we have that lingering packet, we reply to the original\r
+ * step or continue packet.\r
+ * \r
+ * Executing monitor commands can bring the target in and\r
+ * out of the running state so we'll see lots of TARGET_EVENT_XXX\r
+ * that are to be ignored.\r
+ */\r
+ if (gdb_connection->frontend_state == TARGET_RUNNING)\r
+ {\r
+ /* stop forwarding log packets! */\r
+ log_remove_callback(gdb_log_callback, connection);\r
+ \r
+ if (gdb_connection->ctrl_c)\r
+ {\r
+ signal = 0x2;\r
+ gdb_connection->ctrl_c = 0;\r
+ }\r
+ else\r
+ {\r
+ signal = gdb_last_signal(target);\r
+ }\r
+\r
+ snprintf(sig_reply, 4, "T%2.2x", signal);\r
+ gdb_put_packet(connection, sig_reply, 3);\r
+ gdb_connection->frontend_state = TARGET_HALTED;\r
+ }\r
+ break;\r
+ case TARGET_EVENT_GDB_PROGRAM:\r
+ gdb_program_handler(target, event, connection->cmd_ctx);\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_new_connection(connection_t *connection)\r
+{\r
+ gdb_connection_t *gdb_connection = malloc(sizeof(gdb_connection_t));\r
+ gdb_service_t *gdb_service = connection->service->priv;\r
+ int retval;\r
+ int initial_ack;\r
+\r
+ connection->priv = gdb_connection;\r
+\r
+ /* initialize gdb connection information */\r
+ gdb_connection->buf_p = gdb_connection->buffer;\r
+ gdb_connection->buf_cnt = 0;\r
+ gdb_connection->ctrl_c = 0;\r
+ gdb_connection->frontend_state = TARGET_HALTED;\r
+ gdb_connection->vflash_image = NULL;\r
+ gdb_connection->closed = 0;\r
+ gdb_connection->busy = 0;\r
+ \r
+ /* output goes through gdb connection */\r
+ command_set_output_handler(connection->cmd_ctx, gdb_output, connection);\r
+\r
+ /* register callback to be informed about target events */\r
+ target_register_event_callback(gdb_target_callback_event_handler, connection); \r
+\r
+ /* a gdb session just attached, put the target in halt mode */\r
+ if (((retval = gdb_service->target->type->halt(gdb_service->target)) != ERROR_OK) &&\r
+ (retval != ERROR_TARGET_ALREADY_HALTED))\r
+ {\r
+ ERROR("error(%d) when trying to halt target, falling back to \"reset halt\"", retval);\r
+ command_run_line(connection->cmd_ctx, "reset halt");\r
+ }\r
+\r
+ /* This will time out after 1 second */\r
+ command_run_line(connection->cmd_ctx, "wait_halt 1");\r
+\r
+ /* remove the initial ACK from the incoming buffer */\r
+ if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK)\r
+ return retval;\r
+\r
+ if (initial_ack != '+')\r
+ gdb_putback_char(connection, initial_ack);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_connection_closed(connection_t *connection)\r
+{\r
+ gdb_service_t *gdb_service = connection->service->priv;\r
+ gdb_connection_t *gdb_connection = connection->priv;\r
+\r
+ /* see if an image built with vFlash commands is left */\r
+ if (gdb_connection->vflash_image)\r
+ {\r
+ image_close(gdb_connection->vflash_image);\r
+ free(gdb_connection->vflash_image);\r
+ gdb_connection->vflash_image = NULL;\r
+ }\r
+\r
+ /* if this connection registered a debug-message receiver delete it */\r
+ delete_debug_msg_receiver(connection->cmd_ctx, gdb_service->target);\r
+ \r
+ if (connection->priv)\r
+ {\r
+ free(connection->priv);\r
+ connection->priv = NULL;\r
+ }\r
+ else\r
+ {\r
+ ERROR("BUG: connection->priv == NULL");\r
+ }\r
+\r
+ target_unregister_event_callback(gdb_target_callback_event_handler, connection);\r
+ log_remove_callback(gdb_log_callback, connection);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+void gdb_send_error(connection_t *connection, u8 the_error)\r
+{\r
+ char err[4];\r
+ snprintf(err, 4, "E%2.2X", the_error );\r
+ gdb_put_packet(connection, err, 3);\r
+}\r
+\r
+int gdb_last_signal_packet(connection_t *connection, target_t *target, char* packet, int packet_size)\r
+{\r
+ char sig_reply[4];\r
+ int signal;\r
+\r
+ signal = gdb_last_signal(target);\r
+\r
+ snprintf(sig_reply, 4, "S%2.2x", signal);\r
+ gdb_put_packet(connection, sig_reply, 3);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+/* Convert register to string of bits. NB! The # of bits in the\r
+ * register might be non-divisible by 8(a byte), in which\r
+ * case an entire byte is shown. */\r
+void gdb_str_to_target(target_t *target, char *tstr, reg_t *reg)\r
+{\r
+ int i;\r
+\r
+ u8 *buf;\r
+ int buf_len;\r
+ buf = reg->value;\r
+ buf_len = CEIL(reg->size, 8); \r
+\r
+ if (target->endianness == TARGET_LITTLE_ENDIAN)\r
+ {\r
+ for (i = 0; i < buf_len; i++)\r
+ {\r
+ tstr[i*2] = DIGITS[(buf[i]>>4) & 0xf];\r
+ tstr[i*2+1] = DIGITS[buf[i]&0xf];\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for (i = 0; i < buf_len; i++)\r
+ {\r
+ tstr[(buf_len-1-i)*2] = DIGITS[(buf[i]>>4)&0xf];\r
+ tstr[(buf_len-1-i)*2+1] = DIGITS[buf[i]&0xf];\r
+ }\r
+ } \r
+}\r
+\r
+void gdb_target_to_str(target_t *target, char *tstr, char *str)\r
+{\r
+ int str_len = strlen(tstr);\r
+ int i;\r
+\r
+ if (str_len % 2)\r
+ {\r
+ ERROR("BUG: gdb value with uneven number of characters encountered");\r
+ exit(-1);\r
+ }\r
+\r
+ if (target->endianness == TARGET_LITTLE_ENDIAN)\r
+ {\r
+ for (i = 0; i < str_len; i+=2)\r
+ {\r
+ str[str_len - i - 1] = tstr[i + 1];\r
+ str[str_len - i - 2] = tstr[i];\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for (i = 0; i < str_len; i++)\r
+ {\r
+ str[i] = tstr[i];\r
+ }\r
+ } \r
+}\r
+\r
+int gdb_get_registers_packet(connection_t *connection, target_t *target, char* packet, int packet_size)\r
+{\r
+ reg_t **reg_list;\r
+ int reg_list_size;\r
+ int retval;\r
+ int reg_packet_size = 0;\r
+ char *reg_packet;\r
+ char *reg_packet_p;\r
+ int i;\r
+\r
+#ifdef _DEBUG_GDB_IO_\r
+ DEBUG("-");\r
+#endif\r
+\r
+ if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)\r
+ {\r
+ switch (retval)\r
+ {\r
+ case ERROR_TARGET_NOT_HALTED:\r
+ ERROR("gdb requested registers but we're not halted, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ default:\r
+ /* this is a bug condition - get_gdb_reg_list() may not return any other error */\r
+ ERROR("BUG: unexpected error returned by get_gdb_reg_list()");\r
+ exit(-1);\r
+ }\r
+ }\r
+\r
+ for (i = 0; i < reg_list_size; i++)\r
+ {\r
+ reg_packet_size += reg_list[i]->size;\r
+ }\r
+\r
+ reg_packet = malloc(CEIL(reg_packet_size, 8) * 2);\r
+ reg_packet_p = reg_packet;\r
+\r
+ for (i = 0; i < reg_list_size; i++)\r
+ {\r
+ gdb_str_to_target(target, reg_packet_p, reg_list[i]);\r
+ reg_packet_p += CEIL(reg_list[i]->size, 8) * 2;\r
+ }\r
+\r
+#ifdef _DEBUG_GDB_IO_\r
+ {\r
+ char *reg_packet_p;\r
+ reg_packet_p = strndup(reg_packet, CEIL(reg_packet_size, 8) * 2);\r
+ DEBUG("reg_packet: %s", reg_packet_p);\r
+ free(reg_packet_p);\r
+ }\r
+#endif\r
+\r
+ gdb_put_packet(connection, reg_packet, CEIL(reg_packet_size, 8) * 2);\r
+ free(reg_packet);\r
+\r
+ free(reg_list);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_set_registers_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
+{\r
+ int i;\r
+ reg_t **reg_list;\r
+ int reg_list_size;\r
+ int retval;\r
+ char *packet_p;\r
+\r
+#ifdef _DEBUG_GDB_IO_\r
+ DEBUG("-");\r
+#endif\r
+\r
+ /* skip command character */\r
+ packet++;\r
+ packet_size--;\r
+\r
+ if (packet_size % 2)\r
+ {\r
+ WARNING("GDB set_registers packet with uneven characters received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)\r
+ {\r
+ switch (retval)\r
+ {\r
+ case ERROR_TARGET_NOT_HALTED:\r
+ ERROR("gdb tried to registers but we're not halted, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ default:\r
+ /* this is a bug condition - get_gdb_reg_list() may not return any other error */\r
+ ERROR("BUG: unexpected error returned by get_gdb_reg_list()");\r
+ exit(-1);\r
+ }\r
+ }\r
+\r
+ packet_p = packet;\r
+ for (i = 0; i < reg_list_size; i++)\r
+ {\r
+ u8 *bin_buf;\r
+ char *hex_buf;\r
+ reg_arch_type_t *arch_type;\r
+\r
+ /* convert from GDB-string (target-endian) to hex-string (big-endian) */\r
+ hex_buf = malloc(CEIL(reg_list[i]->size, 8) * 2);\r
+ gdb_target_to_str(target, packet_p, hex_buf);\r
+\r
+ /* convert hex-string to binary buffer */\r
+ bin_buf = malloc(CEIL(reg_list[i]->size, 8));\r
+ str_to_buf(hex_buf, CEIL(reg_list[i]->size, 8) * 2, bin_buf, reg_list[i]->size, 16);\r
+\r
+ /* get register arch_type, and call set method */ \r
+ arch_type = register_get_arch_type(reg_list[i]->arch_type);\r
+ if (arch_type == NULL)\r
+ {\r
+ ERROR("BUG: encountered unregistered arch type");\r
+ exit(-1);\r
+ }\r
+ arch_type->set(reg_list[i], bin_buf);\r
+\r
+ /* advance packet pointer */ \r
+ packet_p += (CEIL(reg_list[i]->size, 8) * 2);\r
+\r
+ free(bin_buf);\r
+ free(hex_buf);\r
+ }\r
+\r
+ /* free reg_t *reg_list[] array allocated by get_gdb_reg_list */ \r
+ free(reg_list);\r
+\r
+ gdb_put_packet(connection, "OK", 2);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_get_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
+{\r
+ char *reg_packet;\r
+ int reg_num = strtoul(packet + 1, NULL, 16);\r
+ reg_t **reg_list;\r
+ int reg_list_size;\r
+ int retval;\r
+\r
+#ifdef _DEBUG_GDB_IO_\r
+ DEBUG("-");\r
+#endif\r
+\r
+ if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)\r
+ {\r
+ switch (retval)\r
+ {\r
+ case ERROR_TARGET_NOT_HALTED:\r
+ ERROR("gdb requested registers but we're not halted, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ default:\r
+ /* this is a bug condition - get_gdb_reg_list() may not return any other error */\r
+ ERROR("BUG: unexpected error returned by get_gdb_reg_list()");\r
+ exit(-1);\r
+ }\r
+ }\r
+\r
+ if (reg_list_size <= reg_num)\r
+ {\r
+ ERROR("gdb requested a non-existing register");\r
+ exit(-1);\r
+ }\r
+\r
+ reg_packet = malloc(CEIL(reg_list[reg_num]->size, 8) * 2);\r
+\r
+ gdb_str_to_target(target, reg_packet, reg_list[reg_num]);\r
+\r
+ gdb_put_packet(connection, reg_packet, CEIL(reg_list[reg_num]->size, 8) * 2);\r
+\r
+ free(reg_list);\r
+ free(reg_packet);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_set_register_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
+{\r
+ char *separator;\r
+ char *hex_buf;\r
+ u8 *bin_buf;\r
+ int reg_num = strtoul(packet + 1, &separator, 16);\r
+ reg_t **reg_list;\r
+ int reg_list_size;\r
+ int retval;\r
+ reg_arch_type_t *arch_type;\r
+\r
+ DEBUG("-");\r
+\r
+ if ((retval = target->type->get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK)\r
+ {\r
+ switch (retval)\r
+ {\r
+ case ERROR_TARGET_NOT_HALTED:\r
+ ERROR("gdb tried to set a register but we're not halted, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ default:\r
+ /* this is a bug condition - get_gdb_reg_list() may not return any other error */\r
+ ERROR("BUG: unexpected error returned by get_gdb_reg_list()");\r
+ exit(-1);\r
+ }\r
+ }\r
+\r
+ if (reg_list_size < reg_num)\r
+ {\r
+ ERROR("gdb requested a non-existing register");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ if (*separator != '=')\r
+ {\r
+ ERROR("GDB 'set register packet', but no '=' following the register number");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ /* convert from GDB-string (target-endian) to hex-string (big-endian) */\r
+ hex_buf = malloc(CEIL(reg_list[reg_num]->size, 8) * 2);\r
+ gdb_target_to_str(target, separator + 1, hex_buf);\r
+\r
+ /* convert hex-string to binary buffer */\r
+ bin_buf = malloc(CEIL(reg_list[reg_num]->size, 8));\r
+ str_to_buf(hex_buf, CEIL(reg_list[reg_num]->size, 8) * 2, bin_buf, reg_list[reg_num]->size, 16);\r
+\r
+ /* get register arch_type, and call set method */ \r
+ arch_type = register_get_arch_type(reg_list[reg_num]->arch_type);\r
+ if (arch_type == NULL)\r
+ {\r
+ ERROR("BUG: encountered unregistered arch type");\r
+ exit(-1);\r
+ }\r
+ arch_type->set(reg_list[reg_num], bin_buf);\r
+\r
+ gdb_put_packet(connection, "OK", 2);\r
+\r
+ free(bin_buf);\r
+ free(hex_buf);\r
+ free(reg_list);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_memory_packet_error(connection_t *connection, int retval)\r
+{\r
+ switch (retval)\r
+ {\r
+ case ERROR_TARGET_NOT_HALTED:\r
+ ERROR("gdb tried to read memory but we're not halted, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ case ERROR_TARGET_DATA_ABORT:\r
+ gdb_send_error(connection, EIO);\r
+ break;\r
+ case ERROR_TARGET_TRANSLATION_FAULT:\r
+ gdb_send_error(connection, EFAULT);\r
+ break;\r
+ case ERROR_TARGET_UNALIGNED_ACCESS:\r
+ gdb_send_error(connection, EFAULT);\r
+ break;\r
+ default:\r
+ /* This could be that the target reset itself. */\r
+ ERROR("unexpected error %i. Dropping connection.", retval);\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+/* We don't have to worry about the default 2 second timeout for GDB packets,\r
+ * because GDB breaks up large memory reads into smaller reads.\r
+ * \r
+ * 8191 bytes by the looks of it. Why 8191 bytes instead of 8192?????\r
+ */\r
+int gdb_read_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
+{\r
+ char *separator;\r
+ u32 addr = 0;\r
+ u32 len = 0;\r
+\r
+ u8 *buffer;\r
+ char *hex_buffer;\r
+\r
+ int retval = ERROR_OK;\r
+\r
+ /* skip command character */\r
+ packet++;\r
+\r
+ addr = strtoul(packet, &separator, 16);\r
+\r
+ if (*separator != ',')\r
+ {\r
+ ERROR("incomplete read memory packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ len = strtoul(separator+1, NULL, 16);\r
+\r
+ buffer = malloc(len);\r
+\r
+ DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);\r
+\r
+ retval = target_read_buffer(target, addr, len, buffer);\r
+\r
+ if ((retval == ERROR_TARGET_DATA_ABORT) && (!gdb_report_data_abort))\r
+ {\r
+ /* TODO : Here we have to lie and send back all zero's lest stack traces won't work.\r
+ * At some point this might be fixed in GDB, in which case this code can be removed.\r
+ * \r
+ * OpenOCD developers are acutely aware of this problem, but there is nothing\r
+ * gained by involving the user in this problem that hopefully will get resolved\r
+ * eventually\r
+ * \r
+ * http://sourceware.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gdb&pr=2395\r
+ *\r
+ * For now, the default is to fix up things to make current GDB versions work.\r
+ * This can be overwritten using the gdb_report_data_abort <'enable'|'disable'> command.\r
+ */\r
+ memset(buffer, 0, len);\r
+ retval = ERROR_OK;\r
+ }\r
+\r
+ if (retval == ERROR_OK)\r
+ {\r
+ hex_buffer = malloc(len * 2 + 1);\r
+\r
+ int i;\r
+ for (i = 0; i < len; i++)\r
+ {\r
+ u8 t = buffer[i];\r
+ hex_buffer[2 * i] = DIGITS[(t >> 4) & 0xf];\r
+ hex_buffer[2 * i + 1] = DIGITS[t & 0xf];\r
+ }\r
+\r
+ gdb_put_packet(connection, hex_buffer, len * 2);\r
+\r
+ free(hex_buffer);\r
+ }\r
+ else\r
+ {\r
+ retval = gdb_memory_packet_error(connection, retval);\r
+ }\r
+\r
+ free(buffer);\r
+\r
+ return retval;\r
+}\r
+\r
+int gdb_write_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
+{\r
+ char *separator;\r
+ u32 addr = 0;\r
+ u32 len = 0;\r
+\r
+ u8 *buffer;\r
+\r
+ int i;\r
+ int retval;\r
+\r
+ /* skip command character */\r
+ packet++;\r
+\r
+ addr = strtoul(packet, &separator, 16);\r
+\r
+ if (*separator != ',')\r
+ {\r
+ ERROR("incomplete write memory packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ len = strtoul(separator+1, &separator, 16);\r
+\r
+ if (*(separator++) != ':')\r
+ {\r
+ ERROR("incomplete write memory packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ buffer = malloc(len);\r
+\r
+ DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);\r
+\r
+ for (i=0; i<len; i++)\r
+ {\r
+ u32 tmp;\r
+ sscanf(separator + 2*i, "%2x", &tmp);\r
+ buffer[i] = tmp;\r
+ }\r
+\r
+ retval = target_write_buffer(target, addr, len, buffer);\r
+\r
+ if (retval == ERROR_OK)\r
+ {\r
+ gdb_put_packet(connection, "OK", 2);\r
+ }\r
+ else\r
+ {\r
+ if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)\r
+ return retval; \r
+ }\r
+\r
+ free(buffer);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_write_memory_binary_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
+{\r
+ char *separator;\r
+ u32 addr = 0;\r
+ u32 len = 0;\r
+\r
+ int retval;\r
+\r
+ /* skip command character */\r
+ packet++;\r
+\r
+ addr = strtoul(packet, &separator, 16);\r
+\r
+ if (*separator != ',')\r
+ {\r
+ ERROR("incomplete write memory binary packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ len = strtoul(separator+1, &separator, 16);\r
+\r
+ if (*(separator++) != ':')\r
+ {\r
+ ERROR("incomplete write memory binary packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ retval = ERROR_OK;\r
+ if (len)\r
+ {\r
+ DEBUG("addr: 0x%8.8x, len: 0x%8.8x", addr, len);\r
+\r
+ retval = target_write_buffer(target, addr, len, (u8*)separator);\r
+ }\r
+\r
+ if (retval == ERROR_OK)\r
+ {\r
+ gdb_put_packet(connection, "OK", 2);\r
+ }\r
+ else\r
+ {\r
+ if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)\r
+ return retval; \r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+void gdb_step_continue_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
+{\r
+ int current = 0;\r
+ u32 address = 0x0;\r
+\r
+ DEBUG("-");\r
+\r
+ if (packet_size > 1)\r
+ {\r
+ packet[packet_size] = 0;\r
+ address = strtoul(packet + 1, NULL, 16);\r
+ }\r
+ else\r
+ {\r
+ current = 1;\r
+ }\r
+\r
+ if (packet[0] == 'c')\r
+ {\r
+ DEBUG("continue");\r
+ target->type->resume(target, current, address, 0, 0); /* resume at current address, don't handle breakpoints, not debugging */\r
+ }\r
+ else if (packet[0] == 's')\r
+ {\r
+ DEBUG("step");\r
+ target->type->step(target, current, address, 0); /* step at current or address, don't handle breakpoints */\r
+ }\r
+}\r
+\r
+int gdb_bp_wp_packet_error(connection_t *connection, int retval)\r
+{\r
+ switch (retval)\r
+ {\r
+ case ERROR_TARGET_NOT_HALTED:\r
+ ERROR("gdb tried to set a breakpoint but we're not halted, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ break;\r
+ case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:\r
+ gdb_send_error(connection, EBUSY);\r
+ break;\r
+ default:\r
+ ERROR("BUG: unexpected error %i", retval);\r
+ exit(-1);\r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_breakpoint_watchpoint_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
+{\r
+ int type;\r
+ enum breakpoint_type bp_type = BKPT_SOFT /* dummy init to avoid warning */;\r
+ enum watchpoint_rw wp_type;\r
+ u32 address;\r
+ u32 size;\r
+ char *separator;\r
+ int retval;\r
+\r
+ DEBUG("-");\r
+\r
+ type = strtoul(packet + 1, &separator, 16);\r
+\r
+ if (type == 0) /* memory breakpoint */\r
+ bp_type = BKPT_SOFT;\r
+ else if (type == 1) /* hardware breakpoint */\r
+ bp_type = BKPT_HARD;\r
+ else if (type == 2) /* write watchpoint */\r
+ wp_type = WPT_WRITE;\r
+ else if (type == 3) /* read watchpoint */\r
+ wp_type = WPT_READ;\r
+ else if (type == 4) /* access watchpoint */\r
+ wp_type = WPT_ACCESS;\r
+\r
+ if (*separator != ',')\r
+ {\r
+ ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ address = strtoul(separator+1, &separator, 16);\r
+\r
+ if (*separator != ',')\r
+ {\r
+ ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ size = strtoul(separator+1, &separator, 16);\r
+\r
+ switch (type)\r
+ {\r
+ case 0:\r
+ case 1:\r
+ if (packet[0] == 'Z')\r
+ {\r
+ if ((retval = breakpoint_add(target, address, size, bp_type)) != ERROR_OK)\r
+ {\r
+ if ((retval = gdb_bp_wp_packet_error(connection, retval)) != ERROR_OK)\r
+ return retval;\r
+ }\r
+ else\r
+ {\r
+ gdb_put_packet(connection, "OK", 2);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ breakpoint_remove(target, address);\r
+ gdb_put_packet(connection, "OK", 2);\r
+ }\r
+ break;\r
+ case 2:\r
+ case 3:\r
+ case 4:\r
+ {\r
+ if (packet[0] == 'Z')\r
+ {\r
+ if ((retval = watchpoint_add(target, address, size, type-2, 0, 0xffffffffu)) != ERROR_OK)\r
+ {\r
+ if ((retval = gdb_bp_wp_packet_error(connection, retval)) != ERROR_OK)\r
+ return retval;\r
+ }\r
+ else\r
+ {\r
+ gdb_put_packet(connection, "OK", 2);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ watchpoint_remove(target, address);\r
+ gdb_put_packet(connection, "OK", 2);\r
+ }\r
+ break;\r
+ }\r
+ default:\r
+ break;\r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+/* print out a string and allocate more space as needed, mainly used for XML at this point */\r
+void xml_printf(int *retval, char **xml, int *pos, int *size, const char *fmt, ...)\r
+{\r
+ if (*retval != ERROR_OK)\r
+ {\r
+ return;\r
+ }\r
+ int first = 1;\r
+ \r
+ for (;;)\r
+ {\r
+ if ((*xml == NULL) || (!first))\r
+ {\r
+ /* start by 0 to exercise all the code paths.\r
+ * Need minimum 2 bytes to fit 1 char and 0 terminator. */\r
+ \r
+ *size = *size * 2 + 2;\r
+ char *t = *xml;\r
+ *xml = realloc(*xml, *size);\r
+ if (*xml == NULL)\r
+ {\r
+ if (t)\r
+ free(t);\r
+ *retval = ERROR_SERVER_REMOTE_CLOSED;\r
+ return;\r
+ }\r
+ }\r
+ \r
+ va_list ap;\r
+ int ret;\r
+ va_start(ap, fmt);\r
+ ret = vsnprintf(*xml + *pos, *size - *pos, fmt, ap);\r
+ va_end(ap);\r
+ if ((ret > 0) && ((ret + 1) < *size - *pos))\r
+ {\r
+ *pos += ret;\r
+ return;\r
+ }\r
+ /* there was just enough or not enough space, allocate more. */\r
+ first = 0;\r
+ }\r
+}\r
+\r
+static int decode_xfer_read(char *buf, char **annex, int *ofs, unsigned int *len)\r
+{\r
+ char *separator;\r
+ \r
+ /* Extract and NUL-terminate the annex. */\r
+ *annex = buf;\r
+ while (*buf && *buf != ':')\r
+ buf++;\r
+ if (*buf == '\0')\r
+ return -1;\r
+ *buf++ = 0;\r
+ \r
+ /* After the read marker and annex, qXfer looks like a\r
+ * traditional 'm' packet. */\r
+ \r
+ *ofs = strtoul(buf, &separator, 16);\r
+\r
+ if (*separator != ',')\r
+ return -1;\r
+\r
+ *len = strtoul(separator+1, NULL, 16);\r
+ \r
+ return 0;\r
+}\r
+\r
+int gdb_calc_blocksize(flash_bank_t *bank)\r
+{\r
+ int i;\r
+ int block_size = 0xffffffff;\r
+ \r
+ /* loop through all sectors and return smallest sector size */\r
+ \r
+ for (i = 0; i < bank->num_sectors; i++)\r
+ {\r
+ if (bank->sectors[i].size < block_size)\r
+ block_size = bank->sectors[i].size;\r
+ }\r
+ \r
+ return block_size;\r
+}\r
+\r
+int gdb_query_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
+{\r
+ command_context_t *cmd_ctx = connection->cmd_ctx;\r
+ \r
+ if (strstr(packet, "qRcmd,"))\r
+ {\r
+ if (packet_size > 6)\r
+ {\r
+ char *cmd;\r
+ int i;\r
+ cmd = malloc((packet_size - 6)/2 + 1);\r
+ for (i=0; i < (packet_size - 6)/2; i++)\r
+ {\r
+ u32 tmp;\r
+ sscanf(packet + 6 + 2*i, "%2x", &tmp);\r
+ cmd[i] = tmp;\r
+ }\r
+ cmd[(packet_size - 6)/2] = 0x0;\r
+ \r
+ /* We want to print all debug output to GDB connection */\r
+ log_add_callback(gdb_log_callback, connection);\r
+ target_call_timer_callbacks();\r
+ command_run_line(cmd_ctx, cmd);\r
+ free(cmd);\r
+ }\r
+ gdb_put_packet(connection, "OK", 2);\r
+ return ERROR_OK;\r
+ }\r
+ else if (strstr(packet, "qCRC:"))\r
+ {\r
+ if (packet_size > 5)\r
+ {\r
+ int retval;\r
+ char gdb_reply[10];\r
+ char *separator;\r
+ u32 checksum;\r
+ u32 addr = 0;\r
+ u32 len = 0;\r
+ \r
+ /* skip command character */\r
+ packet += 5;\r
+ \r
+ addr = strtoul(packet, &separator, 16);\r
+ \r
+ if (*separator != ',')\r
+ {\r
+ ERROR("incomplete read memory packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+ \r
+ len = strtoul(separator + 1, NULL, 16);\r
+ \r
+ retval = target_checksum_memory(target, addr, len, &checksum);\r
+ \r
+ if (retval == ERROR_OK)\r
+ {\r
+ snprintf(gdb_reply, 10, "C%8.8x", checksum);\r
+ gdb_put_packet(connection, gdb_reply, 9);\r
+ }\r
+ else\r
+ {\r
+ if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK)\r
+ return retval; \r
+ }\r
+ \r
+ return ERROR_OK;\r
+ }\r
+ }\r
+ else if (strstr(packet, "qSupported"))\r
+ {\r
+ /* we currently support packet size and qXfer:memory-map:read (if enabled)\r
+ * disable qXfer:features:read for the moment */\r
+ int retval = ERROR_OK;\r
+ char *buffer = NULL;\r
+ int pos = 0;\r
+ int size = 0;\r
+\r
+ xml_printf(&retval, &buffer, &pos, &size, \r
+ "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read-",\r
+ (GDB_BUFFER_SIZE - 1), gdb_use_memory_map == 1 ? '+' : '-');\r
+ \r
+ if (retval != ERROR_OK)\r
+ {\r
+ gdb_send_error(connection, 01);\r
+ return ERROR_OK;\r
+ }\r
+ \r
+ gdb_put_packet(connection, buffer, strlen(buffer));\r
+ free(buffer);\r
+ \r
+ return ERROR_OK;\r
+ }\r
+ else if (strstr(packet, "qXfer:memory-map:read::"))\r
+ {\r
+ /* We get away with only specifying flash here. Regions that are not\r
+ * specified are treated as if we provided no memory map(if not we \r
+ * could detect the holes and mark them as RAM).\r
+ * Normally we only execute this code once, but no big deal if we\r
+ * have to regenerate it a couple of times. */\r
+ \r
+ flash_bank_t *p;\r
+ char *xml = NULL;\r
+ int size = 0;\r
+ int pos = 0;\r
+ int retval = ERROR_OK;\r
+ \r
+ int offset;\r
+ int length;\r
+ char *separator;\r
+ int blocksize;\r
+ \r
+ /* skip command character */\r
+ packet += 23;\r
+ \r
+ offset = strtoul(packet, &separator, 16);\r
+ length = strtoul(separator + 1, &separator, 16);\r
+ \r
+ xml_printf(&retval, &xml, &pos, &size, "<memory-map>\n");\r
+ \r
+ int i = 0;\r
+ for (;;)\r
+ {\r
+ p = get_flash_bank_by_num(i);\r
+ if (p == NULL)\r
+ break;\r
+ \r
+ /* if device has uneven sector sizes, eg. str7, lpc\r
+ * we pass the smallest sector size to gdb memory map */\r
+ blocksize = gdb_calc_blocksize(p);\r
+ \r
+ xml_printf(&retval, &xml, &pos, &size, "<memory type=\"flash\" start=\"0x%x\" length=\"0x%x\">\n" \\r
+ "<property name=\"blocksize\">0x%x</property>\n" \\r
+ "</memory>\n", \\r
+ p->base, p->size, blocksize);\r
+ i++;\r
+ }\r
+ \r
+ xml_printf(&retval, &xml, &pos, &size, "</memory-map>\n");\r
+\r
+ if (retval != ERROR_OK)\r
+ {\r
+ gdb_send_error(connection, retval);\r
+ return retval;\r
+ }\r
+ \r
+ if (offset + length > pos)\r
+ {\r
+ length = pos - offset;\r
+ }\r
+\r
+ char *t = malloc(length + 1);\r
+ t[0] = 'l';\r
+ memcpy(t + 1, xml + offset, length);\r
+ gdb_put_packet(connection, t, length + 1);\r
+ \r
+ free(t);\r
+ free(xml);\r
+ return ERROR_OK;\r
+ }\r
+ else if (strstr(packet, "qXfer:features:read:"))\r
+ { \r
+ char *xml = NULL;\r
+ int size = 0;\r
+ int pos = 0;\r
+ int retval = ERROR_OK;\r
+ \r
+ int offset;\r
+ unsigned int length;\r
+ char *annex;\r
+ \r
+ /* skip command character */\r
+ packet += 20;\r
+ \r
+ if (decode_xfer_read(packet, &annex, &offset, &length) < 0)\r
+ {\r
+ gdb_send_error(connection, 01);\r
+ return ERROR_OK;\r
+ }\r
+ \r
+ if (strcmp(annex, "target.xml") != 0)\r
+ {\r
+ gdb_send_error(connection, 01);\r
+ return ERROR_OK;\r
+ }\r
+ \r
+ xml_printf(&retval, &xml, &pos, &size, \\r
+ "l<target version=\"1.0\">\n<architecture>arm</architecture>\n</target>\n");\r
+ \r
+ if (retval != ERROR_OK)\r
+ {\r
+ gdb_send_error(connection, retval);\r
+ return retval;\r
+ }\r
+ \r
+ gdb_put_packet(connection, xml, strlen(xml) + 1);\r
+ \r
+ free(xml);\r
+ return ERROR_OK;\r
+ }\r
+ \r
+ gdb_put_packet(connection, "", 0);\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int packet_size)\r
+{\r
+ gdb_connection_t *gdb_connection = connection->priv;\r
+ gdb_service_t *gdb_service = connection->service->priv;\r
+ int result;\r
+\r
+ /* if flash programming disabled - send a empty reply */\r
+ \r
+ if (gdb_flash_program == 0)\r
+ {\r
+ gdb_put_packet(connection, "", 0);\r
+ return ERROR_OK;\r
+ }\r
+ \r
+ if (strstr(packet, "vFlashErase:"))\r
+ {\r
+ unsigned long addr;\r
+ unsigned long length;\r
+ \r
+ char *parse = packet + 12;\r
+ if (*parse == '\0')\r
+ {\r
+ ERROR("incomplete vFlashErase packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ addr = strtoul(parse, &parse, 16);\r
+\r
+ if (*(parse++) != ',' || *parse == '\0')\r
+ {\r
+ ERROR("incomplete vFlashErase packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+\r
+ length = strtoul(parse, &parse, 16);\r
+\r
+ if (*parse != '\0')\r
+ {\r
+ ERROR("incomplete vFlashErase packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+ \r
+ /* assume all sectors need erasing - stops any problems\r
+ * when flash_write is called multiple times */\r
+ flash_set_dirty();\r
+ \r
+ /* perform any target specific operations before the erase */\r
+ target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_PROGRAM);\r
+ \r
+ /* perform erase */\r
+ if ((result = flash_erase_address_range(gdb_service->target, addr, length)) != ERROR_OK)\r
+ {\r
+ /* GDB doesn't evaluate the actual error number returned,\r
+ * treat a failed erase as an I/O error\r
+ */\r
+ gdb_send_error(connection, EIO);\r
+ ERROR("flash_erase returned %i", result);\r
+ }\r
+ else\r
+ gdb_put_packet(connection, "OK", 2);\r
+ \r
+ return ERROR_OK;\r
+ }\r
+\r
+ if (strstr(packet, "vFlashWrite:"))\r
+ {\r
+ unsigned long addr;\r
+ unsigned long length;\r
+ char *parse = packet + 12;\r
+\r
+ if (*parse == '\0')\r
+ {\r
+ ERROR("incomplete vFlashErase packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+ addr = strtoul(parse, &parse, 16);\r
+ if (*(parse++) != ':')\r
+ {\r
+ ERROR("incomplete vFlashErase packet received, dropping connection");\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+ length = packet_size - (parse - packet);\r
+ \r
+ /* create a new image if there isn't already one */\r
+ if (gdb_connection->vflash_image == NULL)\r
+ {\r
+ gdb_connection->vflash_image = malloc(sizeof(image_t));\r
+ image_open(gdb_connection->vflash_image, "", "build");\r
+ }\r
+\r
+ /* create new section with content from packet buffer */\r
+ image_add_section(gdb_connection->vflash_image, addr, length, 0x0, (u8*)parse);\r
+\r
+ gdb_put_packet(connection, "OK", 2);\r
+\r
+ return ERROR_OK;\r
+ }\r
+\r
+ if (!strcmp(packet, "vFlashDone"))\r
+ {\r
+ u32 written;\r
+\r
+ /* process the flashing buffer. No need to erase as GDB\r
+ * always issues a vFlashErase first. */\r
+ if ((result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, 0)) != ERROR_OK)\r
+ {\r
+ if (result == ERROR_FLASH_DST_OUT_OF_BANK)\r
+ gdb_put_packet(connection, "E.memtype", 9);\r
+ else\r
+ gdb_send_error(connection, EIO);\r
+ }\r
+ else\r
+ {\r
+ DEBUG("wrote %u bytes from vFlash image to flash", written);\r
+ gdb_put_packet(connection, "OK", 2);\r
+ }\r
+ \r
+ image_close(gdb_connection->vflash_image);\r
+ free(gdb_connection->vflash_image);\r
+ gdb_connection->vflash_image = NULL;\r
+ \r
+ return ERROR_OK;\r
+ }\r
+\r
+ gdb_put_packet(connection, "", 0);\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_detach(connection_t *connection, target_t *target)\r
+{\r
+ switch( detach_mode )\r
+ {\r
+ case GDB_DETACH_RESUME:\r
+ target->type->resume(target, 1, 0, 1, 0);\r
+ break;\r
+ \r
+ case GDB_DETACH_RESET:\r
+ target_process_reset(connection->cmd_ctx);\r
+ break;\r
+ \r
+ case GDB_DETACH_HALT:\r
+ target->type->halt(target);\r
+ break;\r
+ \r
+ case GDB_DETACH_NOTHING:\r
+ break;\r
+ }\r
+ \r
+ gdb_put_packet(connection, "OK", 2);\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+static void gdb_log_callback(void *priv, const char *file, int line, \r
+ const char *function, const char *format, va_list args)\r
+{\r
+ connection_t *connection = priv;\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+ \r
+ if (gdb_con->busy)\r
+ {\r
+ /* do not reply this using the O packet */\r
+ return;\r
+ }\r
+\r
+ char *t = alloc_printf(format, args);\r
+ if (t == NULL)\r
+ return;\r
+ \r
+ gdb_output_con(connection, t); \r
+ \r
+ free(t);\r
+}\r
+\r
+int gdb_input_inner(connection_t *connection)\r
+{\r
+ gdb_service_t *gdb_service = connection->service->priv;\r
+ target_t *target = gdb_service->target;\r
+ char packet[GDB_BUFFER_SIZE];\r
+ int packet_size;\r
+ int retval;\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+ static int extended_protocol = 0;\r
+\r
+ /* drain input buffer */\r
+ do\r
+ {\r
+ packet_size = GDB_BUFFER_SIZE-1;\r
+ if ((retval = gdb_get_packet(connection, packet, &packet_size)) != ERROR_OK)\r
+ {\r
+ return retval;\r
+ }\r
+\r
+ /* terminate with zero */\r
+ packet[packet_size] = 0;\r
+\r
+ DEBUG("received packet: '%s'", packet);\r
+\r
+ if (packet_size > 0)\r
+ {\r
+ retval = ERROR_OK;\r
+ switch (packet[0])\r
+ {\r
+ case 'H':\r
+ /* Hct... -- set thread \r
+ * we don't have threads, send empty reply */\r
+ gdb_put_packet(connection, NULL, 0);\r
+ break;\r
+ case 'q':\r
+ retval = gdb_query_packet(connection, target, packet, packet_size);\r
+ break;\r
+ case 'g':\r
+ retval = gdb_get_registers_packet(connection, target, packet, packet_size);\r
+ break;\r
+ case 'G':\r
+ retval = gdb_set_registers_packet(connection, target, packet, packet_size);\r
+ break;\r
+ case 'p':\r
+ retval = gdb_get_register_packet(connection, target, packet, packet_size);\r
+ break;\r
+ case 'P':\r
+ retval = gdb_set_register_packet(connection, target, packet, packet_size);\r
+ break;\r
+ case 'm':\r
+ retval = gdb_read_memory_packet(connection, target, packet, packet_size);\r
+ break;\r
+ case 'M':\r
+ retval = gdb_write_memory_packet(connection, target, packet, packet_size);\r
+ break;\r
+ case 'z':\r
+ case 'Z':\r
+ retval = gdb_breakpoint_watchpoint_packet(connection, target, packet, packet_size);\r
+ break;\r
+ case '?':\r
+ gdb_last_signal_packet(connection, target, packet, packet_size);\r
+ break;\r
+ case 'c':\r
+ case 's':\r
+ {\r
+ /* We're running/stepping, in which case we can \r
+ * forward log output until the target is halted */\r
+ gdb_connection_t *gdb_con = connection->priv;\r
+ gdb_con->frontend_state = TARGET_RUNNING;\r
+ log_add_callback(gdb_log_callback, connection);\r
+ gdb_step_continue_packet(connection, target, packet, packet_size);\r
+ }\r
+ break;\r
+ case 'v':\r
+ retval = gdb_v_packet(connection, target, packet, packet_size);\r
+ break;\r
+ case 'D':\r
+ retval = gdb_detach(connection, target);\r
+ extended_protocol = 0;\r
+ break;\r
+ case 'X':\r
+ if ((retval = gdb_write_memory_binary_packet(connection, target, packet, packet_size)) != ERROR_OK)\r
+ return retval;\r
+ break;\r
+ case 'k':\r
+ if (extended_protocol != 0)\r
+ break;\r
+ gdb_put_packet(connection, "OK", 2);\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ case '!':\r
+ /* handle extended remote protocol */\r
+ extended_protocol = 1;\r
+ gdb_put_packet(connection, "OK", 2);\r
+ break;\r
+ case 'R':\r
+ /* handle extended restart packet */\r
+ target_process_reset(connection->cmd_ctx);\r
+ break;\r
+ default:\r
+ /* ignore unkown packets */\r
+ DEBUG("ignoring 0x%2.2x packet", packet[0]);\r
+ gdb_put_packet(connection, NULL, 0);\r
+ break;\r
+ }\r
+\r
+ /* if a packet handler returned an error, exit input loop */\r
+ if (retval != ERROR_OK)\r
+ return retval;\r
+ }\r
+\r
+ if (gdb_con->ctrl_c)\r
+ {\r
+ if (target->state == TARGET_RUNNING)\r
+ {\r
+ target->type->halt(target);\r
+ gdb_con->ctrl_c = 0;\r
+ }\r
+ }\r
+\r
+ } while (gdb_con->buf_cnt > 0);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_input(connection_t *connection)\r
+{\r
+ int retval = gdb_input_inner(connection);\r
+ if (retval == ERROR_SERVER_REMOTE_CLOSED)\r
+ return retval;\r
+ /* we'll recover from any other errors(e.g. temporary timeouts, etc.) */\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_init()\r
+{\r
+ gdb_service_t *gdb_service;\r
+ target_t *target = targets;\r
+ int i = 0;\r
+\r
+ if (!target)\r
+ {\r
+ WARNING("no gdb ports allocated as no target has been specified");\r
+ return ERROR_OK;\r
+ }\r
+\r
+ if (gdb_port == 0)\r
+ {\r
+ WARNING("no gdb port specified, using default port 3333");\r
+ gdb_port = 3333;\r
+ }\r
+\r
+ while (target)\r
+ {\r
+ char service_name[8];\r
+\r
+ snprintf(service_name, 8, "gdb-%2.2i", i);\r
+\r
+ gdb_service = malloc(sizeof(gdb_service_t));\r
+ gdb_service->target = target;\r
+\r
+ add_service("gdb", CONNECTION_GDB, gdb_port + i, 1, gdb_new_connection, gdb_input, gdb_connection_closed, gdb_service);\r
+\r
+ DEBUG("gdb service for target %s at port %i", target->type->name, gdb_port + i);\r
+\r
+ i++;\r
+ target = target->next;\r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+/* daemon configuration command gdb_port */\r
+int handle_gdb_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
+{\r
+ if (argc == 0)\r
+ return ERROR_OK;\r
+\r
+ /* only if the port wasn't overwritten by cmdline */\r
+ if (gdb_port == 0)\r
+ gdb_port = strtoul(args[0], NULL, 0);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int handle_gdb_detach_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
+{\r
+ if (argc == 1)\r
+ {\r
+ if (strcmp(args[0], "resume") == 0)\r
+ {\r
+ detach_mode = GDB_DETACH_RESUME;\r
+ return ERROR_OK;\r
+ }\r
+ else if (strcmp(args[0], "reset") == 0)\r
+ {\r
+ detach_mode = GDB_DETACH_RESET;\r
+ return ERROR_OK;\r
+ }\r
+ else if (strcmp(args[0], "halt") == 0)\r
+ {\r
+ detach_mode = GDB_DETACH_HALT;\r
+ return ERROR_OK;\r
+ }\r
+ else if (strcmp(args[0], "nothing") == 0)\r
+ {\r
+ detach_mode = GDB_DETACH_NOTHING;\r
+ return ERROR_OK;\r
+ }\r
+ }\r
+ \r
+ WARNING("invalid gdb_detach configuration directive: %s", args[0]);\r
+ return ERROR_OK;\r
+}\r
+\r
+int handle_gdb_memory_map_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
+{\r
+ if (argc == 1)\r
+ {\r
+ if (strcmp(args[0], "enable") == 0)\r
+ {\r
+ gdb_use_memory_map = 1;\r
+ return ERROR_OK;\r
+ }\r
+ else if (strcmp(args[0], "disable") == 0)\r
+ {\r
+ gdb_use_memory_map = 0;\r
+ return ERROR_OK;\r
+ }\r
+ }\r
+ \r
+ WARNING("invalid gdb_memory_map configuration directive: %s", args[0]);\r
+ return ERROR_OK;\r
+}\r
+\r
+int handle_gdb_flash_program_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
+{\r
+ if (argc == 1)\r
+ {\r
+ if (strcmp(args[0], "enable") == 0)\r
+ {\r
+ gdb_flash_program = 1;\r
+ return ERROR_OK;\r
+ }\r
+ else if (strcmp(args[0], "disable") == 0)\r
+ {\r
+ gdb_flash_program = 0;\r
+ return ERROR_OK;\r
+ }\r
+ }\r
+ \r
+ WARNING("invalid gdb_memory_map configuration directive: %s", args[0]);\r
+ return ERROR_OK;\r
+}\r
+\r
+int handle_gdb_report_data_abort_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
+{\r
+ if (argc == 1)\r
+ {\r
+ if (strcmp(args[0], "enable") == 0)\r
+ {\r
+ gdb_report_data_abort = 1;\r
+ return ERROR_OK;\r
+ }\r
+ else if (strcmp(args[0], "disable") == 0)\r
+ {\r
+ gdb_report_data_abort = 0;\r
+ return ERROR_OK;\r
+ }\r
+ }\r
+ \r
+ WARNING("invalid gdb_report_data_abort configuration directive: %s", args[0]);\r
+ return ERROR_OK;\r
+}\r
+\r
+int gdb_register_commands(command_context_t *command_context)\r
+{\r
+ register_command(command_context, NULL, "gdb_port", handle_gdb_port_command,\r
+ COMMAND_CONFIG, "");\r
+ register_command(command_context, NULL, "gdb_detach", handle_gdb_detach_command,\r
+ COMMAND_CONFIG, "");\r
+ register_command(command_context, NULL, "gdb_memory_map", handle_gdb_memory_map_command,\r
+ COMMAND_CONFIG, "");\r
+ register_command(command_context, NULL, "gdb_flash_program", handle_gdb_flash_program_command,\r
+ COMMAND_CONFIG, "");\r
+ register_command(command_context, NULL, "gdb_report_data_abort", handle_gdb_report_data_abort_command,\r
+ COMMAND_CONFIG, "");\r
+ return ERROR_OK;\r
+}\r
-/***************************************************************************
- * Copyright (C) 2005 by Dominic Rath *
- * Dominic.Rath@gmx.de *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the *
- * Free Software Foundation, Inc., *
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
- ***************************************************************************/
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "replacements.h"
-
-#include "telnet_server.h"
-
-#include "server.h"
-#include "log.h"
-#include "command.h"
-#include "target.h"
-#include "target_request.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <ctype.h>
-
-static unsigned short telnet_port = 0;
-
-int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
-int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
-
-static char *negotiate =
- "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */
- "\xFF\xFB\x01" /* IAC WILL Echo */
- "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */
- "\xFF\xFE\x01"; /* IAC DON'T Echo */
-
-#define CTRL(c) (c - '@')
-
-/* The only way we can detect that the socket is closed is the first time
- * we write to it, we will fail. Subsequent write operations will
- * succeed. Shudder!
- */
-int telnet_write(connection_t *connection, void *data, int len)
-{
- telnet_connection_t *t_con = connection->priv;
- if (t_con->closed)
- return ERROR_SERVER_REMOTE_CLOSED;
-
- if (write_socket(connection->fd, data, len) == len)
- {
- return ERROR_OK;
- }
- t_con->closed = 1;
- return ERROR_SERVER_REMOTE_CLOSED;
-}
-
-int telnet_prompt(connection_t *connection)
-{
- telnet_connection_t *t_con = connection->priv;
-
- return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));
-}
-
-int telnet_outputline(connection_t *connection, char* line)
-{
- telnet_write(connection, line, strlen(line));
- return telnet_write(connection, "\r\n\0", 3);
-}
-
-int telnet_output(struct command_context_s *cmd_ctx, char* line)
-{
- connection_t *connection = cmd_ctx->output_handler_priv;
-
- return telnet_outputline(connection, line);
-}
-
-void telnet_log_callback(void *priv, const char *file, int line,
- const char *function, const char *format, va_list args)
-{
- connection_t *connection = priv;
- char *t = allocPrintf(format, args);
- char *t2;
- if (t == NULL)
- return;
- t2=t;
- char *endline;
- do
- {
- if ((endline=strchr(t2, '\n'))!=NULL)
- {
- *endline=0;
- }
- telnet_outputline(connection, t2);
- t2=endline+1;
- } while (endline);
-
-
- free(t);
-}
-
-int telnet_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)
-{
- struct command_context_s *cmd_ctx = priv;
- connection_t *connection = cmd_ctx->output_handler_priv;
- telnet_connection_t *t_con = connection->priv;
-
- switch (event)
- {
- case TARGET_EVENT_HALTED:
- target_arch_state(target);
- if (!t_con->suppress_prompt)
- telnet_prompt(connection);
- break;
- case TARGET_EVENT_RESUMED:
- if (!t_con->suppress_prompt)
- telnet_prompt(connection);
- break;
- default:
- break;
- }
-
- return ERROR_OK;
-}
-
-int telnet_new_connection(connection_t *connection)
-{
- telnet_connection_t *telnet_connection = malloc(sizeof(telnet_connection_t));
- telnet_service_t *telnet_service = connection->service->priv;
- int i;
-
- connection->priv = telnet_connection;
-
- /* initialize telnet connection information */
- telnet_connection->closed = 0;
- telnet_connection->line_size = 0;
- telnet_connection->line_cursor = 0;
- telnet_connection->option_size = 0;
- telnet_connection->prompt = strdup("> ");
- telnet_connection->suppress_prompt = 0;
- telnet_connection->state = TELNET_STATE_DATA;
-
- /* output goes through telnet connection */
- command_set_output_handler(connection->cmd_ctx, telnet_output, connection);
-
- /* negotiate telnet options */
- telnet_write(connection, negotiate, strlen(negotiate));
-
- /* print connection banner */
- if (telnet_service->banner)
- {
- telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));
- telnet_write(connection, "\r\n\0", 3);
- }
-
- telnet_prompt(connection);
-
- /* initialize history */
- for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
- {
- telnet_connection->history[i] = NULL;
- }
- telnet_connection->next_history = 0;
- telnet_connection->current_history = 0;
-
- target_register_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
-
- return ERROR_OK;
-}
-
-void telnet_clear_line(connection_t *connection, telnet_connection_t *t_con)
-{
- /* move to end of line */
- if (t_con->line_cursor < t_con->line_size)
- {
- telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
- }
-
- /* backspace, overwrite with space, backspace */
- while (t_con->line_size > 0)
- {
- telnet_write(connection, "\b \b", 3);
- t_con->line_size--;
- }
- t_con->line_cursor = 0;
-}
-
-int telnet_input(connection_t *connection)
-{
- int bytes_read;
- char buffer[TELNET_BUFFER_SIZE];
- char *buf_p;
- telnet_connection_t *t_con = connection->priv;
- command_context_t *command_context = connection->cmd_ctx;
-
- bytes_read = read_socket(connection->fd, buffer, TELNET_BUFFER_SIZE);
-
- if (bytes_read == 0)
- return ERROR_SERVER_REMOTE_CLOSED;
- else if (bytes_read == -1)
- {
- ERROR("error during read: %s", strerror(errno));
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- buf_p = buffer;
- while (bytes_read)
- {
- switch (t_con->state)
- {
- case TELNET_STATE_DATA:
- if (*buf_p == '\xff')
- {
- t_con->state = TELNET_STATE_IAC;
- }
- else
- {
- if (isprint(*buf_p)) /* printable character */
- {
- telnet_write(connection, buf_p, 1);
- if (t_con->line_cursor == t_con->line_size)
- {
- t_con->line[t_con->line_size++] = *buf_p;
- t_con->line_cursor++;
- }
- else
- {
- int i;
- memmove(t_con->line + t_con->line_cursor + 1, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
- t_con->line[t_con->line_cursor++] = *buf_p;
- t_con->line_size++;
- telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
- for (i = t_con->line_cursor; i < t_con->line_size; i++)
- {
- telnet_write(connection, "\b", 1);
- }
- }
- }
- else /* non-printable */
- {
- if (*buf_p == 0x1b) /* escape */
- {
- t_con->state = TELNET_STATE_ESCAPE;
- t_con->last_escape = '\x00';
- }
- else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */
- {
- int retval;
-
- /* skip over combinations with CR/LF + NUL */
- if (((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)) && (bytes_read > 1))
- {
- buf_p++;
- bytes_read--;
- }
- if ((*(buf_p + 1) == 0) && (bytes_read > 1))
- {
- buf_p++;
- bytes_read--;
- }
- t_con->line[t_con->line_size] = 0;
-
- telnet_write(connection, "\r\n\x00", 3);
-
- if (strcmp(t_con->line, "history") == 0)
- {
- int i;
- for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
- {
- if (t_con->history[i])
- {
- telnet_write(connection, t_con->history[i], strlen(t_con->history[i]));
- telnet_write(connection, "\r\n\x00", 3);
- }
- }
- telnet_prompt(connection);
- t_con->line_size = 0;
- t_con->line_cursor = 0;
- continue;
- }
-
- log_setCallback(telnet_log_callback, connection);
- t_con->suppress_prompt = 1;
-
- if ((retval = command_run_line(command_context, t_con->line)) != ERROR_OK)
- {
- if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
- {
- return ERROR_SERVER_REMOTE_CLOSED;
- }
- }
-
- t_con->suppress_prompt = 0;
-
- /* Save only non-blank lines in the history */
- if (t_con->line_size > 0)
- {
- /* if the history slot is already taken, free it */
- if (t_con->history[t_con->next_history])
- {
- free(t_con->history[t_con->next_history]);
- }
-
- /* add line to history */
- t_con->history[t_con->next_history] = strdup(t_con->line);
-
- /* wrap history at TELNET_LINE_HISTORY_SIZE */
- t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;
-
- /* current history line starts at the new entry */
- t_con->current_history = t_con->next_history;
-
- if (t_con->history[t_con->current_history])
- {
- free(t_con->history[t_con->current_history]);
- }
- t_con->history[t_con->current_history] = strdup("");
- }
-
- int t = telnet_prompt(connection);
- if (t == ERROR_SERVER_REMOTE_CLOSED)
- return t;
-
- t_con->line_size = 0;
- t_con->line_cursor = 0;
- }
- else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) /* delete character */
- {
- if (t_con->line_cursor > 0)
- {
- if (t_con->line_cursor != t_con->line_size)
- {
- int i;
- telnet_write(connection, "\b", 1);
- t_con->line_cursor--;
- t_con->line_size--;
- memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
-
- telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
- telnet_write(connection, " \b", 2);
- for (i = t_con->line_cursor; i < t_con->line_size; i++)
- {
- telnet_write(connection, "\b", 1);
- }
- }
- else
- {
- t_con->line_size--;
- t_con->line_cursor--;
- /* back space: move the 'printer' head one char back, overwrite with space, move back again */
- telnet_write(connection, "\b \b", 3);
- }
- }
- }
- else if (*buf_p == 0x15) /* clear line */
- {
- telnet_clear_line(connection, t_con);
- }
- else if (*buf_p == CTRL('B')) /* cursor left */
- {
- if (t_con->line_cursor > 0)
- {
- telnet_write(connection, "\b", 1);
- t_con->line_cursor--;
- }
- t_con->state = TELNET_STATE_DATA;
- }
- else if (*buf_p == CTRL('F')) /* cursor right */
- {
- if (t_con->line_cursor < t_con->line_size)
- {
- telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
- }
- t_con->state = TELNET_STATE_DATA;
- }
- else
- {
- DEBUG("unhandled nonprintable: %2.2x", *buf_p);
- }
- }
- }
- break;
- case TELNET_STATE_IAC:
- switch (*buf_p)
- {
- case '\xfe':
- t_con->state = TELNET_STATE_DONT;
- break;
- case '\xfd':
- t_con->state = TELNET_STATE_DO;
- break;
- case '\xfc':
- t_con->state = TELNET_STATE_WONT;
- break;
- case '\xfb':
- t_con->state = TELNET_STATE_WILL;
- break;
- }
- break;
- case TELNET_STATE_SB:
- break;
- case TELNET_STATE_SE:
- break;
- case TELNET_STATE_WILL:
- case TELNET_STATE_WONT:
- case TELNET_STATE_DO:
- case TELNET_STATE_DONT:
- t_con->state = TELNET_STATE_DATA;
- break;
- case TELNET_STATE_ESCAPE:
- if (t_con->last_escape == '[')
- {
- if (*buf_p == 'D') /* cursor left */
- {
- if (t_con->line_cursor > 0)
- {
- telnet_write(connection, "\b", 1);
- t_con->line_cursor--;
- }
- t_con->state = TELNET_STATE_DATA;
- }
- else if (*buf_p == 'C') /* cursor right */
- {
- if (t_con->line_cursor < t_con->line_size)
- {
- telnet_write(connection, t_con->line + t_con->line_cursor++, 1);
- }
- t_con->state = TELNET_STATE_DATA;
- }
- else if (*buf_p == 'A') /* cursor up */
- {
- int last_history = (t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;
- if (t_con->history[last_history])
- {
- telnet_clear_line(connection, t_con);
- t_con->line_size = strlen(t_con->history[last_history]);
- t_con->line_cursor = t_con->line_size;
- memcpy(t_con->line, t_con->history[last_history], t_con->line_size + 1);
- telnet_write(connection, t_con->line, t_con->line_size);
- t_con->current_history = last_history;
- }
- t_con->state = TELNET_STATE_DATA;
- }
- else if (*buf_p == 'B') /* cursor down */
- {
- int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;
- if (t_con->history[next_history])
- {
- telnet_clear_line(connection, t_con);
- t_con->line_size = strlen(t_con->history[next_history]);
- t_con->line_cursor = t_con->line_size;
- memcpy(t_con->line, t_con->history[next_history], t_con->line_size + 1);
- telnet_write(connection, t_con->line, t_con->line_size);
- t_con->current_history = next_history;
- }
- t_con->state = TELNET_STATE_DATA;
- }
- else if (*buf_p == '3')
- {
- t_con->last_escape = *buf_p;
- }
- else
- {
- t_con->state = TELNET_STATE_DATA;
- }
- }
- else if (t_con->last_escape == '3')
- {
- /* Remove character */
- if (*buf_p == '~')
- {
- if (t_con->line_cursor < t_con->line_size)
- {
- int i;
- t_con->line_size--;
- /* remove char from line buffer */
- memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);
-
- /* print remainder of buffer */
- telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);
- /* overwrite last char with whitespace */
- telnet_write(connection, " \b", 2);
-
- /* move back to cursor position*/
- for (i = t_con->line_cursor; i < t_con->line_size; i++)
- {
- telnet_write(connection, "\b", 1);
- }
- }
-
- t_con->state = TELNET_STATE_DATA;
- }
- else
- {
- t_con->state = TELNET_STATE_DATA;
- }
- }
- else if (t_con->last_escape == '\x00')
- {
- if (*buf_p == '[')
- {
- t_con->last_escape = *buf_p;
- }
- else
- {
- t_con->state = TELNET_STATE_DATA;
- }
- }
- else
- {
- ERROR("BUG: unexpected value in t_con->last_escape");
- t_con->state = TELNET_STATE_DATA;
- }
-
- break;
- default:
- ERROR("unknown telnet state");
- exit(-1);
- }
-
- bytes_read--;
- buf_p++;
- }
-
- return ERROR_OK;
-}
-
-int telnet_connection_closed(connection_t *connection)
-{
- telnet_connection_t *t_con = connection->priv;
- int i;
-
- if (t_con->prompt)
- {
- free(t_con->prompt);
- t_con->prompt = NULL;
- }
-
- for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
- {
- if (t_con->history[i])
- {
- free(t_con->history[i]);
- t_con->history[i] = NULL;
- }
- }
-
- /* if this connection registered a debug-message receiver delete it */
- delete_debug_msg_receiver(connection->cmd_ctx, NULL);
-
- if (connection->priv)
- {
- free(connection->priv);
- connection->priv = NULL;
- }
- else
- {
- ERROR("BUG: connection->priv == NULL");
- }
-
- target_unregister_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);
-
- return ERROR_OK;
-}
-
-int telnet_set_prompt(connection_t *connection, char *prompt)
-{
- telnet_connection_t *t_con = connection->priv;
-
- t_con->prompt = strdup(prompt);
-
- return ERROR_OK;
-}
-
-int telnet_init(char *banner)
-{
- telnet_service_t *telnet_service = malloc(sizeof(telnet_service_t));
-
- if (telnet_port == 0)
- {
- WARNING("no telnet port specified, using default port 4444");
- telnet_port = 4444;
- }
-
- telnet_service->banner = banner;
-
- add_service("telnet", CONNECTION_TELNET, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service);
-
- return ERROR_OK;
-}
-
-int telnet_register_commands(command_context_t *command_context)
-{
- register_command(command_context, NULL, "exit", handle_exit_command,
- COMMAND_EXEC, "exit telnet session");
-
- register_command(command_context, NULL, "telnet_port", handle_telnet_port_command,
- COMMAND_CONFIG, "");
-
- return ERROR_OK;
-}
-
-/* daemon configuration command telnet_port */
-int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- if (argc == 0)
- return ERROR_OK;
-
- /* only if the port wasn't overwritten by cmdline */
- if (telnet_port == 0)
- telnet_port = strtoul(args[0], NULL, 0);
-
- return ERROR_OK;
-}
-
-int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
- return ERROR_COMMAND_CLOSE_CONNECTION;
-}
+/***************************************************************************\r
+ * Copyright (C) 2005 by Dominic Rath *\r
+ * Dominic.Rath@gmx.de *\r
+ * *\r
+ * This program is free software; you can redistribute it and/or modify *\r
+ * it under the terms of the GNU General Public License as published by *\r
+ * the Free Software Foundation; either version 2 of the License, or *\r
+ * (at your option) any later version. *\r
+ * *\r
+ * This program is distributed in the hope that it will be useful, *\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *\r
+ * GNU General Public License for more details. *\r
+ * *\r
+ * You should have received a copy of the GNU General Public License *\r
+ * along with this program; if not, write to the *\r
+ * Free Software Foundation, Inc., *\r
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *\r
+ ***************************************************************************/\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#include "replacements.h"\r
+\r
+#include "telnet_server.h"\r
+\r
+#include "server.h"\r
+#include "log.h"\r
+#include "command.h"\r
+#include "target.h"\r
+#include "target_request.h"\r
+\r
+#include <stdlib.h>\r
+#include <unistd.h>\r
+#include <errno.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+\r
+static unsigned short telnet_port = 0;\r
+\r
+int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
+int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
+\r
+static char *negotiate =\r
+ "\xFF\xFB\x03" /* IAC WILL Suppress Go Ahead */\r
+ "\xFF\xFB\x01" /* IAC WILL Echo */\r
+ "\xFF\xFD\x03" /* IAC DO Suppress Go Ahead */\r
+ "\xFF\xFE\x01"; /* IAC DON'T Echo */\r
+ \r
+#define CTRL(c) (c - '@')\r
+ \r
+/* The only way we can detect that the socket is closed is the first time\r
+ * we write to it, we will fail. Subsequent write operations will\r
+ * succeed. Shudder!\r
+ */\r
+int telnet_write(connection_t *connection, void *data, int len)\r
+{\r
+ telnet_connection_t *t_con = connection->priv;\r
+ if (t_con->closed)\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+\r
+ if (write_socket(connection->fd, data, len) == len)\r
+ {\r
+ return ERROR_OK;\r
+ }\r
+ t_con->closed = 1;\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+}\r
+\r
+int telnet_prompt(connection_t *connection)\r
+{\r
+ telnet_connection_t *t_con = connection->priv;\r
+\r
+ return telnet_write(connection, t_con->prompt, strlen(t_con->prompt));\r
+}\r
+\r
+int telnet_outputline(connection_t *connection, char* line)\r
+{\r
+ telnet_write(connection, line, strlen(line));\r
+ return telnet_write(connection, "\r\n\0", 3);\r
+}\r
+\r
+int telnet_output(struct command_context_s *cmd_ctx, char* line)\r
+{\r
+ connection_t *connection = cmd_ctx->output_handler_priv;\r
+ \r
+ return telnet_outputline(connection, line);\r
+}\r
+\r
+void telnet_log_callback(void *priv, const char *file, int line, \r
+ const char *function, const char *format, va_list args)\r
+{\r
+ connection_t *connection = priv;\r
+ char *t = alloc_printf(format, args);\r
+ char *t2;\r
+ if (t == NULL)\r
+ return;\r
+ t2=t;\r
+ char *endline;\r
+ do \r
+ {\r
+ if ((endline=strchr(t2, '\n'))!=NULL)\r
+ {\r
+ *endline=0;\r
+ }\r
+ telnet_outputline(connection, t2);\r
+ t2=endline+1;\r
+ } while (endline);\r
+ \r
+ free(t);\r
+}\r
+\r
+int telnet_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv)\r
+{\r
+ struct command_context_s *cmd_ctx = priv;\r
+ connection_t *connection = cmd_ctx->output_handler_priv;\r
+ telnet_connection_t *t_con = connection->priv;\r
+ \r
+ switch (event)\r
+ {\r
+ case TARGET_EVENT_HALTED:\r
+ target_arch_state(target);\r
+ if (!t_con->suppress_prompt)\r
+ telnet_prompt(connection);\r
+ break;\r
+ case TARGET_EVENT_RESUMED:\r
+ if (!t_con->suppress_prompt)\r
+ telnet_prompt(connection);\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int telnet_new_connection(connection_t *connection)\r
+{\r
+ telnet_connection_t *telnet_connection = malloc(sizeof(telnet_connection_t));\r
+ telnet_service_t *telnet_service = connection->service->priv;\r
+ int i;\r
+ \r
+ connection->priv = telnet_connection;\r
+ \r
+ /* initialize telnet connection information */\r
+ telnet_connection->closed = 0;\r
+ telnet_connection->line_size = 0;\r
+ telnet_connection->line_cursor = 0;\r
+ telnet_connection->option_size = 0;\r
+ telnet_connection->prompt = strdup("> ");\r
+ telnet_connection->suppress_prompt = 0;\r
+ telnet_connection->state = TELNET_STATE_DATA;\r
+ \r
+ /* output goes through telnet connection */\r
+ command_set_output_handler(connection->cmd_ctx, telnet_output, connection);\r
+ \r
+ /* negotiate telnet options */\r
+ telnet_write(connection, negotiate, strlen(negotiate));\r
+ \r
+ /* print connection banner */\r
+ if (telnet_service->banner)\r
+ {\r
+ telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner));\r
+ telnet_write(connection, "\r\n\0", 3);\r
+ }\r
+ \r
+ telnet_prompt(connection);\r
+ \r
+ /* initialize history */\r
+ for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)\r
+ {\r
+ telnet_connection->history[i] = NULL;\r
+ }\r
+ telnet_connection->next_history = 0;\r
+ telnet_connection->current_history = 0;\r
+\r
+ target_register_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+void telnet_clear_line(connection_t *connection, telnet_connection_t *t_con)\r
+{\r
+ /* move to end of line */\r
+ if (t_con->line_cursor < t_con->line_size)\r
+ {\r
+ telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);\r
+ }\r
+ \r
+ /* backspace, overwrite with space, backspace */\r
+ while (t_con->line_size > 0)\r
+ {\r
+ telnet_write(connection, "\b \b", 3);\r
+ t_con->line_size--;\r
+ }\r
+ t_con->line_cursor = 0;\r
+}\r
+\r
+int telnet_input(connection_t *connection)\r
+{\r
+ int bytes_read;\r
+ char buffer[TELNET_BUFFER_SIZE];\r
+ char *buf_p;\r
+ telnet_connection_t *t_con = connection->priv;\r
+ command_context_t *command_context = connection->cmd_ctx;\r
+ \r
+ bytes_read = read_socket(connection->fd, buffer, TELNET_BUFFER_SIZE);\r
+ \r
+ if (bytes_read == 0)\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ else if (bytes_read == -1)\r
+ {\r
+ ERROR("error during read: %s", strerror(errno));\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+ \r
+ buf_p = buffer;\r
+ while (bytes_read)\r
+ {\r
+ switch (t_con->state)\r
+ {\r
+ case TELNET_STATE_DATA:\r
+ if (*buf_p == '\xff')\r
+ {\r
+ t_con->state = TELNET_STATE_IAC;\r
+ }\r
+ else\r
+ {\r
+ if (isprint(*buf_p)) /* printable character */\r
+ {\r
+ telnet_write(connection, buf_p, 1);\r
+ if (t_con->line_cursor == t_con->line_size)\r
+ {\r
+ t_con->line[t_con->line_size++] = *buf_p;\r
+ t_con->line_cursor++;\r
+ }\r
+ else\r
+ {\r
+ int i;\r
+ memmove(t_con->line + t_con->line_cursor + 1, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);\r
+ t_con->line[t_con->line_cursor++] = *buf_p;\r
+ t_con->line_size++;\r
+ telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);\r
+ for (i = t_con->line_cursor; i < t_con->line_size; i++)\r
+ {\r
+ telnet_write(connection, "\b", 1);\r
+ }\r
+ }\r
+ }\r
+ else /* non-printable */\r
+ {\r
+ if (*buf_p == 0x1b) /* escape */\r
+ {\r
+ t_con->state = TELNET_STATE_ESCAPE;\r
+ t_con->last_escape = '\x00';\r
+ }\r
+ else if ((*buf_p == 0xd) || (*buf_p == 0xa)) /* CR/LF */\r
+ {\r
+ int retval;\r
+ \r
+ /* skip over combinations with CR/LF + NUL */\r
+ if (((*(buf_p + 1) == 0xa) || (*(buf_p + 1) == 0xd)) && (bytes_read > 1))\r
+ {\r
+ buf_p++;\r
+ bytes_read--;\r
+ }\r
+ if ((*(buf_p + 1) == 0) && (bytes_read > 1))\r
+ {\r
+ buf_p++;\r
+ bytes_read--;\r
+ }\r
+ t_con->line[t_con->line_size] = 0;\r
+ \r
+ telnet_write(connection, "\r\n\x00", 3);\r
+ \r
+ if (strcmp(t_con->line, "history") == 0)\r
+ {\r
+ int i;\r
+ for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)\r
+ {\r
+ if (t_con->history[i])\r
+ {\r
+ telnet_write(connection, t_con->history[i], strlen(t_con->history[i]));\r
+ telnet_write(connection, "\r\n\x00", 3);\r
+ }\r
+ }\r
+ telnet_prompt(connection);\r
+ t_con->line_size = 0;\r
+ t_con->line_cursor = 0;\r
+ continue;\r
+ }\r
+ \r
+ log_add_callback(telnet_log_callback, connection);\r
+ t_con->suppress_prompt = 1;\r
+\r
+ retval = command_run_line(command_context, t_con->line);\r
+ \r
+ log_remove_callback(telnet_log_callback, connection);\r
+ t_con->suppress_prompt = 0;\r
+\r
+ if (retval == ERROR_COMMAND_CLOSE_CONNECTION)\r
+ {\r
+ return ERROR_SERVER_REMOTE_CLOSED;\r
+ }\r
+ \r
+ /* Save only non-blank lines in the history */\r
+ if (t_con->line_size > 0)\r
+ {\r
+ /* if the history slot is already taken, free it */\r
+ if (t_con->history[t_con->next_history])\r
+ {\r
+ free(t_con->history[t_con->next_history]);\r
+ }\r
+ \r
+ /* add line to history */\r
+ t_con->history[t_con->next_history] = strdup(t_con->line);\r
+\r
+ /* wrap history at TELNET_LINE_HISTORY_SIZE */\r
+ t_con->next_history = (t_con->next_history + 1) % TELNET_LINE_HISTORY_SIZE;\r
+ \r
+ /* current history line starts at the new entry */\r
+ t_con->current_history = t_con->next_history;\r
+ \r
+ if (t_con->history[t_con->current_history])\r
+ {\r
+ free(t_con->history[t_con->current_history]);\r
+ }\r
+ t_con->history[t_con->current_history] = strdup("");\r
+ }\r
+ \r
+ int t = telnet_prompt(connection);\r
+ if (t == ERROR_SERVER_REMOTE_CLOSED)\r
+ return t;\r
+ \r
+ t_con->line_size = 0;\r
+ t_con->line_cursor = 0;\r
+ }\r
+ else if ((*buf_p == 0x7f) || (*buf_p == 0x8)) /* delete character */\r
+ {\r
+ if (t_con->line_cursor > 0)\r
+ {\r
+ if (t_con->line_cursor != t_con->line_size)\r
+ {\r
+ int i;\r
+ telnet_write(connection, "\b", 1);\r
+ t_con->line_cursor--;\r
+ t_con->line_size--;\r
+ memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);\r
+ \r
+ telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);\r
+ telnet_write(connection, " \b", 2);\r
+ for (i = t_con->line_cursor; i < t_con->line_size; i++)\r
+ {\r
+ telnet_write(connection, "\b", 1);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ t_con->line_size--;\r
+ t_con->line_cursor--;\r
+ /* back space: move the 'printer' head one char back, overwrite with space, move back again */\r
+ telnet_write(connection, "\b \b", 3);\r
+ }\r
+ }\r
+ }\r
+ else if (*buf_p == 0x15) /* clear line */\r
+ {\r
+ telnet_clear_line(connection, t_con);\r
+ }\r
+ else if (*buf_p == CTRL('B')) /* cursor left */\r
+ {\r
+ if (t_con->line_cursor > 0)\r
+ {\r
+ telnet_write(connection, "\b", 1);\r
+ t_con->line_cursor--;\r
+ }\r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ else if (*buf_p == CTRL('F')) /* cursor right */\r
+ {\r
+ if (t_con->line_cursor < t_con->line_size)\r
+ {\r
+ telnet_write(connection, t_con->line + t_con->line_cursor++, 1);\r
+ }\r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ else\r
+ {\r
+ DEBUG("unhandled nonprintable: %2.2x", *buf_p);\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ case TELNET_STATE_IAC:\r
+ switch (*buf_p)\r
+ {\r
+ case '\xfe':\r
+ t_con->state = TELNET_STATE_DONT;\r
+ break;\r
+ case '\xfd':\r
+ t_con->state = TELNET_STATE_DO;\r
+ break;\r
+ case '\xfc':\r
+ t_con->state = TELNET_STATE_WONT;\r
+ break;\r
+ case '\xfb':\r
+ t_con->state = TELNET_STATE_WILL;\r
+ break;\r
+ }\r
+ break;\r
+ case TELNET_STATE_SB:\r
+ break;\r
+ case TELNET_STATE_SE:\r
+ break;\r
+ case TELNET_STATE_WILL:\r
+ case TELNET_STATE_WONT:\r
+ case TELNET_STATE_DO:\r
+ case TELNET_STATE_DONT:\r
+ t_con->state = TELNET_STATE_DATA;\r
+ break;\r
+ case TELNET_STATE_ESCAPE:\r
+ if (t_con->last_escape == '[')\r
+ {\r
+ if (*buf_p == 'D') /* cursor left */\r
+ {\r
+ if (t_con->line_cursor > 0)\r
+ {\r
+ telnet_write(connection, "\b", 1);\r
+ t_con->line_cursor--;\r
+ }\r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ else if (*buf_p == 'C') /* cursor right */\r
+ {\r
+ if (t_con->line_cursor < t_con->line_size)\r
+ {\r
+ telnet_write(connection, t_con->line + t_con->line_cursor++, 1);\r
+ }\r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ else if (*buf_p == 'A') /* cursor up */\r
+ {\r
+ int last_history = (t_con->current_history > 0) ? t_con->current_history - 1 : TELNET_LINE_HISTORY_SIZE-1;\r
+ if (t_con->history[last_history])\r
+ {\r
+ telnet_clear_line(connection, t_con);\r
+ t_con->line_size = strlen(t_con->history[last_history]);\r
+ t_con->line_cursor = t_con->line_size;\r
+ memcpy(t_con->line, t_con->history[last_history], t_con->line_size + 1);\r
+ telnet_write(connection, t_con->line, t_con->line_size);\r
+ t_con->current_history = last_history;\r
+ }\r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ else if (*buf_p == 'B') /* cursor down */\r
+ {\r
+ int next_history = (t_con->current_history + 1) % TELNET_LINE_HISTORY_SIZE;\r
+ if (t_con->history[next_history])\r
+ {\r
+ telnet_clear_line(connection, t_con);\r
+ t_con->line_size = strlen(t_con->history[next_history]);\r
+ t_con->line_cursor = t_con->line_size;\r
+ memcpy(t_con->line, t_con->history[next_history], t_con->line_size + 1);\r
+ telnet_write(connection, t_con->line, t_con->line_size);\r
+ t_con->current_history = next_history;\r
+ }\r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ else if (*buf_p == '3')\r
+ {\r
+ t_con->last_escape = *buf_p;\r
+ }\r
+ else\r
+ {\r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ }\r
+ else if (t_con->last_escape == '3')\r
+ {\r
+ /* Remove character */\r
+ if (*buf_p == '~')\r
+ {\r
+ if (t_con->line_cursor < t_con->line_size)\r
+ {\r
+ int i;\r
+ t_con->line_size--;\r
+ /* remove char from line buffer */\r
+ memmove(t_con->line + t_con->line_cursor, t_con->line + t_con->line_cursor + 1, t_con->line_size - t_con->line_cursor);\r
+ \r
+ /* print remainder of buffer */\r
+ telnet_write(connection, t_con->line + t_con->line_cursor, t_con->line_size - t_con->line_cursor);\r
+ /* overwrite last char with whitespace */\r
+ telnet_write(connection, " \b", 2);\r
+ \r
+ /* move back to cursor position*/\r
+ for (i = t_con->line_cursor; i < t_con->line_size; i++)\r
+ {\r
+ telnet_write(connection, "\b", 1);\r
+ }\r
+ }\r
+ \r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ else\r
+ {\r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ }\r
+ else if (t_con->last_escape == '\x00')\r
+ {\r
+ if (*buf_p == '[')\r
+ {\r
+ t_con->last_escape = *buf_p;\r
+ }\r
+ else\r
+ {\r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ ERROR("BUG: unexpected value in t_con->last_escape");\r
+ t_con->state = TELNET_STATE_DATA;\r
+ }\r
+ \r
+ break;\r
+ default:\r
+ ERROR("unknown telnet state");\r
+ exit(-1);\r
+ }\r
+\r
+ bytes_read--;\r
+ buf_p++;\r
+ }\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+int telnet_connection_closed(connection_t *connection)\r
+{\r
+ telnet_connection_t *t_con = connection->priv;\r
+ int i;\r
+ \r
+ if (t_con->prompt)\r
+ {\r
+ free(t_con->prompt);\r
+ t_con->prompt = NULL;\r
+ }\r
+ \r
+ for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)\r
+ {\r
+ if (t_con->history[i])\r
+ {\r
+ free(t_con->history[i]);\r
+ t_con->history[i] = NULL;\r
+ }\r
+ }\r
+ \r
+ /* if this connection registered a debug-message receiver delete it */\r
+ delete_debug_msg_receiver(connection->cmd_ctx, NULL);\r
+ \r
+ if (connection->priv)\r
+ {\r
+ free(connection->priv);\r
+ connection->priv = NULL;\r
+ }\r
+ else\r
+ {\r
+ ERROR("BUG: connection->priv == NULL");\r
+ }\r
+ \r
+ target_unregister_event_callback(telnet_target_callback_event_handler, connection->cmd_ctx);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int telnet_set_prompt(connection_t *connection, char *prompt)\r
+{\r
+ telnet_connection_t *t_con = connection->priv;\r
+\r
+ t_con->prompt = strdup(prompt);\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+int telnet_init(char *banner)\r
+{\r
+ telnet_service_t *telnet_service = malloc(sizeof(telnet_service_t));\r
+ \r
+ if (telnet_port == 0)\r
+ {\r
+ WARNING("no telnet port specified, using default port 4444");\r
+ telnet_port = 4444;\r
+ }\r
+ \r
+ telnet_service->banner = banner;\r
+ \r
+ add_service("telnet", CONNECTION_TELNET, telnet_port, 1, telnet_new_connection, telnet_input, telnet_connection_closed, telnet_service);\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+int telnet_register_commands(command_context_t *command_context)\r
+{\r
+ register_command(command_context, NULL, "exit", handle_exit_command,\r
+ COMMAND_EXEC, "exit telnet session");\r
+ \r
+ register_command(command_context, NULL, "telnet_port", handle_telnet_port_command,\r
+ COMMAND_CONFIG, "");\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+/* daemon configuration command telnet_port */\r
+int handle_telnet_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
+{\r
+ if (argc == 0)\r
+ return ERROR_OK;\r
+\r
+ /* only if the port wasn't overwritten by cmdline */\r
+ if (telnet_port == 0)\r
+ telnet_port = strtoul(args[0], NULL, 0);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
+{\r
+ return ERROR_COMMAND_CLOSE_CONNECTION;\r
+}\r