Patchwork [1/1] Support gdbstub qXfer:spaces features.

login
register
mail settings
Submitter Alexander Barabash
Date Jan. 7, 2013, 1:31 p.m.
Message ID <1357565466-18472-1-git-send-email-alexander_barabash@mentor.com>
Download mbox | patch
Permalink /patch/209921/
State New
Headers show

Comments

Alexander Barabash - Jan. 7, 2013, 1:31 p.m.
Support qXfer:spaces:read and qXfer:spaces:write.
These gdbstub features allow GDB to access target's
hardware registers.

Signed-off-by: Alexander Barabash <alexander_barabash@mentor.com>
---
 Makefile.target |    1 +
 gdbstub.c       |  176 ++++++++++++++++++++++-
 xfer-spaces.c   |  419 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 xfer-spaces.h   |   74 ++++++++++
 4 files changed, 667 insertions(+), 3 deletions(-)
 create mode 100644 xfer-spaces.c
 create mode 100644 xfer-spaces.h

Patch

diff --git a/Makefile.target b/Makefile.target
index 5bfa4960..361ef2b 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -109,6 +109,7 @@  CONFIG_NO_GET_MEMORY_MAPPING = $(if $(subst n,,$(CONFIG_HAVE_GET_MEMORY_MAPPING)
 CONFIG_NO_CORE_DUMP = $(if $(subst n,,$(CONFIG_HAVE_CORE_DUMP)),n,y)
 
 obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o
+obj-y += xfer-spaces.o
 obj-y += hw/
 obj-$(CONFIG_KVM) += kvm-all.o
 obj-$(CONFIG_NO_KVM) += kvm-stub.o
diff --git a/gdbstub.c b/gdbstub.c
index a8dd437..f1c2208 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -18,6 +18,7 @@ 
  */
 #include "config.h"
 #include "qemu-common.h"
+#include "xfer-spaces.h"
 #ifdef CONFIG_USER_ONLY
 #include <stdlib.h>
 #include <stdio.h>
@@ -1798,6 +1799,35 @@  static int memtox(char *buf, const char *mem, int len)
     return p - buf;
 }
 
+/* Decode data using the encoding for 'x' packets.  */
+static int xtomem(uint8_t *mem, const char *buf, int len)
+{
+    uint8_t *p = mem;
+    char c;
+    bool escaped = false;
+
+    while (len--) {
+        c = *(buf++);
+        if (escaped) {
+            escaped = false;
+            *(p++) = c ^ 0x20;
+        } else {
+            if (c == '}') {
+                escaped = true;
+            } else {
+                *(p++) = c;
+            }
+        }
+    }
+
+    if (escaped) {
+        fprintf(stderr, "Unmatched escape character in target response.\n");
+        return 0;
+    }
+
+    return p - mem;
+}
+
 static const char *get_feature_xml(const char *p, const char **newp)
 {
     size_t len;
@@ -1832,6 +1862,9 @@  static const char *get_feature_xml(const char *p, const char **newp)
         }
         return target_xml;
     }
+    if (strncmp(p, "xfer-spaces.xml", len) == 0) {
+        return xfer_spaces_get_xml();
+    }
     for (i = 0; ; i++) {
         name = xml_builtin[i][0];
         if (!name || (strncmp(name, p, len) == 0 && strlen(name) == len))
@@ -2058,7 +2091,8 @@  static CPUArchState *find_cpu(uint32_t thread_id)
     return NULL;
 }
 
-static int gdb_handle_packet(GDBState *s, const char *line_buf)
+static int gdb_handle_packet(GDBState *s, const char *line_buf,
+                             const char *line_buf_end)
 {
     CPUArchState *env;
     const char *p;
@@ -2424,7 +2458,9 @@  static int gdb_handle_packet(GDBState *s, const char *line_buf)
         if (strncmp(p, "Supported", 9) == 0) {
             snprintf(buf, sizeof(buf), "PacketSize=%x", MAX_PACKET_LENGTH);
 #ifdef GDB_CORE_XML
-            pstrcat(buf, sizeof(buf), ";qXfer:features:read+");
+            pstrcat(buf, sizeof(buf),
+                    ";qXfer:features:read+"
+                    ";qXfer:spaces:read+;qXfer:spaces:write+");
 #endif
             put_packet(s, buf);
             break;
@@ -2468,6 +2504,139 @@  static int gdb_handle_packet(GDBState *s, const char *line_buf)
             put_packet_binary(s, buf, len + 1);
             break;
         }
+
+        if (strncmp(p, "Xfer:spaces:read:", strlen("Xfer:spaces:read:")) == 0) {
+            bool request_wellformed = false;
+            const char *space_name;
+            uint64_t offset;
+            uint64_t length;
+            char **colon_tokens;
+            const char *offset_and_length;
+            char **offset_and_length_tokens = NULL;
+            char *offset_string;
+            char *length_string;
+
+            p += strlen("Xfer:spaces:read:");
+            colon_tokens = g_strsplit(p, ":", 2);
+            do {
+                space_name = colon_tokens[0];
+                if (space_name == NULL) {
+                    break;
+                }
+                offset_and_length = colon_tokens[1];
+                if (offset_and_length == NULL) {
+                    break;
+                }
+                offset_and_length_tokens = g_strsplit(offset_and_length,
+                                                      ",", 2);
+                offset_string = offset_and_length_tokens[0];
+                if (offset_string == NULL) {
+                    break;
+                }
+                length_string = offset_and_length_tokens[1];
+                if (length_string == NULL) {
+                    break;
+                }
+
+                offset = g_ascii_strtoull(offset_string, &offset_string, 16);
+                if (offset > G_MAXUINT32) {
+                    break;
+                }
+                if (*offset_string != '\0') {
+                    break;
+                }
+
+                length = g_ascii_strtoull(length_string, &length_string, 16);
+                if (length > MAX_PACKET_LENGTH) {
+                    break;
+                }
+                if (*length_string != '\0') {
+                    break;
+                }
+
+                request_wellformed = true;
+            } while (false);
+
+            if (request_wellformed) {
+                if (xfer_spaces_read(space_name, (unsigned)offset,
+                                     mem_buf, (unsigned)length)) {
+                    buf[0] = 'm';
+                    memtox(buf + 1, (const char *)mem_buf, length);
+                    put_packet_binary(s, buf, length + 1);
+                } else {
+                    put_packet(s, "");
+                }
+            } else {
+                put_packet(s, "E00");
+            }
+
+            if (offset_and_length_tokens != NULL) {
+                g_strfreev(offset_and_length_tokens);
+            }
+            g_strfreev(colon_tokens);
+            break;
+        }
+
+        if (strncmp(p, "Xfer:spaces:write:",
+                    strlen("Xfer:spaces:write:")) == 0) {
+            bool request_wellformed = false;
+            const char *space_name;
+            uint64_t offset;
+            unsigned length;
+            char **colon_tokens;
+            char *offset_string;
+
+            p += strlen("Xfer:spaces:write:");
+            colon_tokens = g_strsplit(p, ":", 3);
+            do {
+                space_name = colon_tokens[0];
+                if (space_name == NULL) {
+                    break;
+                }
+                p += strlen(space_name) + strlen(":");
+
+                offset_string = colon_tokens[1];
+                if (offset_string == NULL) {
+                    break;
+                }
+                p += strlen(offset_string) + strlen(":");
+
+                /*
+                 * The third token is the data to write,
+                 * which may contain NULL-characters.
+                 */
+
+                offset = g_ascii_strtoull(offset_string, &offset_string, 16);
+                if (offset > G_MAXUINT32) {
+                    break;
+                }
+                if (*offset_string != '\0') {
+                    break;
+                }
+
+                length = xtomem(mem_buf, p, line_buf_end - p);
+                if (length == 0) {
+                    break;
+                }
+
+                request_wellformed = true;
+            } while (false);
+
+            if (request_wellformed) {
+                if (xfer_spaces_write(space_name, (unsigned)offset,
+                                      mem_buf, length)) {
+                    sprintf(buf, "%02X", length);
+                    put_packet(s, buf);
+                } else {
+                    put_packet(s, "");
+                }
+            } else {
+                put_packet(s, "E00");
+            }
+
+            g_strfreev(colon_tokens);
+            break;
+        }
 #endif
         /* Unrecognised 'q' command.  */
         goto unknown_command;
@@ -2701,7 +2870,8 @@  static void gdb_read_byte(GDBState *s, int ch)
             } else {
                 reply = '+';
                 put_buffer(s, &reply, 1);
-                s->state = gdb_handle_packet(s, s->line_buf);
+                s->state = gdb_handle_packet(s, s->line_buf,
+                                             s->line_buf + s->line_buf_index);
             }
             break;
         default:
diff --git a/xfer-spaces.c b/xfer-spaces.c
new file mode 100644
index 0000000..702e98b
--- /dev/null
+++ b/xfer-spaces.c
@@ -0,0 +1,419 @@ 
+/*
+ * GDB Stub xfer-spaces support code.
+ *
+ * Copyright (c) 2012 Mentor Graphics Corp.
+ *
+ * Author: Alex Rozenman <alex_rozenman@mentor.com>
+ * Maintainer: Alexander Barabash <alexander_barabash@mentor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "xfer-spaces.h"
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct XferSpace XferSpace;
+typedef struct XferTreeNode XferTreeNode;
+typedef struct XferRecord XferRecord;
+
+static void xfer_spaces_dump_xml(GString *xml);
+
+static XferRecord *get_record(const char *space_name,
+                              unsigned offset,
+                              unsigned length);
+static XferSpace *get_or_create_space(const char *name);
+static const char *get_space_name(XferSpace *space);
+static void space_create_map(XferSpace *space);
+static void space_create_tree(XferSpace *space);
+static void clear_space(XferSpace *space);
+
+static void xml_dump_node_and_siblings(GString *xml,
+                                       XferTreeNode *node,
+                                       unsigned indent);
+static void xml_dump_node(GString *xml,
+                          XferTreeNode *node,
+                          unsigned indent);
+static void merge_record(XferTreeNode *node, XferRecord *record);
+static XferTreeNode *get_or_create_child_node(XferTreeNode *node,
+                                              const char *name);
+static const char *get_node_name(XferTreeNode *node);
+static XferTreeNode *alloc_node(void);
+static void dealloc_node_and_siblings(XferTreeNode *node);
+static void clear_node(XferTreeNode *node);
+
+static XferRecord *alloc_record(XferObject *object);
+
+static void xml_print_indent(GString *xml, unsigned indent);
+static void dealloc_g_string(GString *s);
+
+struct XferSpace {
+    XferSpace *next;
+    GString *name;
+    XferRecord *list;
+    XferTreeNode *tree;
+    XferRecord **map;
+    unsigned map_size;
+};
+
+struct XferTreeNode {
+    XferTreeNode *sibling;
+    XferTreeNode *child;
+    XferRecord *record;
+    GString *name;
+};
+
+struct XferRecord {
+    XferRecord *next;
+    XferObject *object;
+    unsigned offset;
+};
+
+static XferSpace *xfer_spaces;
+
+#define NAME_SEPARATOR "."
+#define INITIAL_OFFSET 0x10
+
+void xfer_spaces_declare_object(const char *space_name, XferObject *object)
+{
+    XferSpace *space;
+    XferRecord *record;
+
+    space = get_or_create_space(space_name);
+    clear_space(space);
+
+    /* Add to the initial list. */
+    record = alloc_record(object);
+    record->next = space->list;
+    space->list = record;
+}
+
+const char *xfer_spaces_get_xml(void)
+{
+    static GString *xml;
+    bool need_dump = false;
+    XferSpace *space;
+
+    if (xml == NULL) {
+        xml = g_string_sized_new(4 * 1024);
+    }
+
+    for (space = xfer_spaces; space; space = space->next) {
+        if (space->map == NULL) {
+            space_create_map(space);
+            need_dump = true;
+        }
+        if (space->tree == NULL) {
+            space_create_tree(space);
+            need_dump = true;
+        }
+    }
+
+    if (need_dump) {
+        g_string_truncate(xml, 0);
+        xfer_spaces_dump_xml(xml);
+    }
+
+    return xml->str;
+}
+
+bool xfer_spaces_read(const char *space_name,
+                      unsigned offset,
+                      uint8_t *data,
+                      unsigned length)
+{
+    XferRecord *record = get_record(space_name, offset, length);
+    if (record == NULL) {
+        return false;
+    }
+    if (record->object->read == NULL) {
+        return false;
+    }
+    return record->object->read(record->object, data);
+}
+
+/* Write operation on object(s) addressed by their space and offset. */
+bool xfer_spaces_write(const char *space_name,
+                       unsigned offset,
+                       const uint8_t *data,
+                       unsigned length)
+{
+    XferRecord *record = get_record(space_name, offset, length);
+    if (record == NULL) {
+        return false;
+    }
+    if (record->object->write == NULL) {
+        return false;
+    }
+    return record->object->write(record->object, data);
+}
+
+static void xfer_spaces_dump_xml(GString *xml)
+{
+    XferSpace *space;
+    const char *space_name;
+    g_string_append(xml, "<?xml version=\"1.0\"?>\n");
+    g_string_append(xml, "<!DOCTYPE feature SYSTEM \"gdb-target.dtd\">\n");
+    g_string_append(xml, "<feature name=\"org.gnu.gdb.xfer-spaces\">\n");
+    for (space = xfer_spaces; space; space = space->next) {
+        space_name = get_space_name(space);
+        g_string_append_printf(xml, "<space annex=\"%s\" name=\"%s\">\n",
+                               space_name, space_name);
+        xml_dump_node_and_siblings(xml, space->tree, 0);
+        g_string_append_printf(xml, "</space>\n");
+    }
+    g_string_append(xml, "</feature>\n");
+}
+
+static XferRecord *get_record(const char *space_name,
+                              unsigned offset,
+                              unsigned length) {
+    XferRecord *record;
+    XferSpace *space = get_or_create_space(space_name);
+
+    if (space->map == NULL) {
+        space_create_map(space);
+    }
+
+    if (offset + length > space->map_size) {
+        return NULL;
+    }
+
+    record = space->map[offset];
+    if (!record) {
+        return NULL;
+    }
+
+    if (record->object->get_size(record->object) != length) {
+        return NULL;
+    }
+
+    return record;
+}
+
+static XferSpace *get_or_create_space(const char *name)
+{
+    XferSpace *space;
+
+    for (space = xfer_spaces; space; space = xfer_spaces->next) {
+        if (strcmp(name, get_space_name(space)) == 0) {
+            return space;
+        }
+    }
+
+    space = (XferSpace *)g_malloc0(sizeof(XferSpace));
+    space->next = xfer_spaces;
+    space->name = g_string_new(name);
+    xfer_spaces = space;
+    return space;
+}
+
+static const char *get_space_name(XferSpace *space)
+{
+    if (space->name != NULL) {
+        return space->name->str;
+    } else {
+        return NULL;
+    }
+}
+
+static void space_create_map(XferSpace *space)
+{
+    XferRecord *record;
+    unsigned offset = INITIAL_OFFSET;
+
+    /* Set offsets and merge into the tree. */
+    for (record = space->list; record; record = record->next) {
+        record->offset = offset;
+        offset += record->object->get_size(record->object);
+    }
+
+    /* Create the map. */
+    space->map_size = offset;
+    space->map =
+        (XferRecord **)g_malloc0(space->map_size * sizeof(*space->map));
+    for (record = space->list; record; record = record->next) {
+        space->map[record->offset] = record;
+    }
+}
+
+static void space_create_tree(XferSpace *space)
+{
+    XferRecord *record;
+
+    /* Create the tree root. */
+    space->tree = alloc_node();
+
+    /* Merge into the tree. */
+    for (record = space->list; record; record = record->next) {
+        merge_record(space->tree, record);
+    }
+}
+
+static void clear_space(XferSpace *space)
+{
+    if (space != NULL) {
+        return;
+    }
+
+    g_free(space->map);
+    space->map = 0;
+    space->map_size = 0;
+
+    dealloc_node_and_siblings(space->tree);
+    space->tree = 0;
+}
+
+static void xml_dump_node_and_siblings(GString *xml,
+                                       XferTreeNode *node,
+                                       unsigned indent)
+{
+    for (; node; node = node->sibling) {
+        xml_dump_node(xml, node, indent);
+    }
+}
+
+static void xml_dump_node(GString *xml,
+                          XferTreeNode *node,
+                          unsigned indent)
+{
+    const char *node_name;
+
+    xml_print_indent(xml, indent);
+    node_name = get_node_name(node);
+    if (node->record) {
+        unsigned data_size =
+            node->record->object->get_size(node->record->object);
+        g_string_append_printf(xml,
+                               "<reg "
+                               "name=\"%s\" "
+                               "offset=\"%d\" "
+                               "bitsize=\"%d\"/>\n",
+                               node_name,
+                               node->record->offset,
+                               data_size * 8);
+    } else {
+        if (node_name != NULL) {
+            g_string_append_printf(xml, "<group name=\"%s\">\n", node_name);
+            xml_dump_node_and_siblings(xml, node->child, indent + 2);
+            xml_print_indent(xml, indent);
+            g_string_append_printf(xml, "</group>\n");
+        } else {
+            xml_dump_node_and_siblings(xml, node->child, indent);
+        }
+    }
+}
+
+static void merge_record(XferTreeNode *node, XferRecord *record)
+{
+    char *child_name;
+    gchar **tokens;
+    gchar **tokens_pointer;
+    GString *path;
+
+    path = g_string_sized_new(32);
+    record->object->get_name(record->object, path);
+
+    tokens = tokens_pointer = g_strsplit(path->str, NAME_SEPARATOR, 0);
+
+    for (child_name = *tokens_pointer; child_name; ++tokens_pointer) {
+        if (*child_name == '\0') {
+            continue;
+        }
+        node = get_or_create_child_node(node, child_name);
+    }
+
+    g_strfreev(tokens);
+    dealloc_g_string(path);
+
+    node->record = record;
+}
+
+static XferTreeNode *get_or_create_child_node(XferTreeNode *node,
+                                              const char *name)
+{
+    XferTreeNode *child;
+    for (child = node->child; child; child = child->sibling) {
+        if (g_strcmp0(name, get_node_name(child)) == 0) {
+            return child;
+        }
+    }
+    child = alloc_node();
+    child->name = g_string_new(name);
+    child->sibling = node->child;
+    node->child = child;
+    return child;
+}
+
+static const char *get_node_name(XferTreeNode *node)
+{
+    if (node->name != NULL) {
+        return node->name->str;
+    } else {
+        return NULL;
+    }
+}
+
+static XferTreeNode *alloc_node(void)
+{
+    return (XferTreeNode *)g_malloc0(sizeof(XferTreeNode));
+}
+
+static void dealloc_node_and_siblings(XferTreeNode *node)
+{
+    while (node) {
+        XferTreeNode *prev_node;
+        clear_node(node);
+        prev_node = node;
+        node = node->sibling;
+        g_free(prev_node);
+    }
+}
+
+static void clear_node(XferTreeNode *node)
+{
+    if (node == NULL) {
+        return;
+    }
+    dealloc_g_string(node->name);
+    dealloc_node_and_siblings(node->child);
+    g_free(node->child);
+}
+
+static XferRecord *alloc_record(XferObject *object)
+{
+    XferRecord *record = (XferRecord *)g_malloc0(sizeof(XferRecord));
+    record->object = object;
+    return record;
+}
+
+static void xml_print_indent(GString *xml, unsigned indent)
+{
+    unsigned i;
+    for (i = 0; i < indent; ++i) {
+        g_string_append(xml, " ");
+    }
+}
+
+static void dealloc_g_string(GString *s)
+{
+    if (s != NULL) {
+        g_string_free(s, true);
+    }
+}
diff --git a/xfer-spaces.h b/xfer-spaces.h
new file mode 100644
index 0000000..a640362
--- /dev/null
+++ b/xfer-spaces.h
@@ -0,0 +1,74 @@ 
+
+/*
+ * GDB Stub xfer-spaces support code.
+ *
+ * Copyright (c) 2012 Mentor Graphics Corp.
+ *
+ * Author: Alex Rozenman <alex_rozenman@mentor.com>
+ * Maintainer: Alexander Barabash <alexander_barabash@mentor.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef XFER_SPACES_H
+#define XFER_SPACES_H
+
+#include <glib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef struct XferObject XferObject;
+
+struct XferObject {
+
+    /* This function shall return the name of the object. The name is
+       considered to be a hierarchical separated by dots.  */
+    void (*get_name)(XferObject *this, GString *output);
+
+    /* This function shall return the data size (bytes) of
+       this object.  */
+    unsigned (*get_size)(XferObject *this);
+
+    /* Read the data contained in the object. Return boolean success status.  */
+    bool (*read)(XferObject *this, uint8_t *buf);
+
+    /* Write the data into the object. Return boolean success status. */
+    bool (*write)(XferObject *this, const uint8_t *buf);
+};
+
+/* Declare an object in a xfer:space. */
+void xfer_spaces_declare_object(const char *space_name,
+                                XferObject *object);
+
+/* Dump xfer:space XML description. */
+const char *xfer_spaces_get_xml(void);
+
+/* Read operation on object(s) addressed by their space and offset. */
+bool xfer_spaces_read(const char *space_name,
+                      unsigned offset,
+                      uint8_t *data,
+                      unsigned length);
+
+/* Write operation on object(s) addressed by their space and offset. */
+bool xfer_spaces_write(const char *space_name,
+                       unsigned offset,
+                       const uint8_t *data,
+                       unsigned length);
+
+#endif /* XFER_SPACES_H */