[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 1/1] Support gdbstub qXfer:spaces features.
From: |
Alexander Barabash |
Subject: |
[Qemu-devel] [PATCH 1/1] Support gdbstub qXfer:spaces features. |
Date: |
Mon, 7 Jan 2013 15:31:06 +0200 |
Support qXfer:spaces:read and qXfer:spaces:write.
These gdbstub features allow GDB to access target's
hardware registers.
Signed-off-by: Alexander Barabash <address@hidden>
---
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
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 <address@hidden>
+ * Maintainer: Alexander Barabash <address@hidden>
+ *
+ * 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 <address@hidden>
+ * Maintainer: Alexander Barabash <address@hidden>
+ *
+ * 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 */
--
1.7.9.5
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] [PATCH 1/1] Support gdbstub qXfer:spaces features.,
Alexander Barabash <=