summaryrefslogtreecommitdiff
path: root/payloads/libpayload/gdb/stub.c
diff options
context:
space:
mode:
Diffstat (limited to 'payloads/libpayload/gdb/stub.c')
-rw-r--r--payloads/libpayload/gdb/stub.c124
1 files changed, 124 insertions, 0 deletions
diff --git a/payloads/libpayload/gdb/stub.c b/payloads/libpayload/gdb/stub.c
new file mode 100644
index 0000000000..73afa70c47
--- /dev/null
+++ b/payloads/libpayload/gdb/stub.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <gdb.h>
+#include <libpayload.h>
+
+struct gdb_state gdb_state;
+
+static u8 reply_buf[2048];
+static u8 command_buf[2048];
+
+static struct gdb_message command = {
+ .buf = command_buf,
+ .used = 0,
+ .size = sizeof(command_buf),
+};
+static struct gdb_message reply = {
+ .buf = reply_buf,
+ .used = 0,
+ .size = sizeof(reply_buf),
+};
+
+void gdb_command_loop(u8 signal)
+{
+ if (gdb_state.resumed) {
+ /* We were just running. Send a stop reply. */
+ reply.used = 0;
+ gdb_message_add_string(&reply, "S");
+ gdb_message_encode_bytes(&reply, &signal, 1);
+ gdb_send_reply(&reply);
+
+ }
+ gdb_state.signal = signal;
+ gdb_state.resumed = 0;
+ gdb_state.connected = 1;
+
+ while (1) {
+ int i;
+
+ gdb_get_command(&command);
+
+ reply.used = 0;
+ for (i = 0; i < gdb_command_count; i++) {
+ int clen = strlen(gdb_commands[i].str);
+ if (!strncmp(gdb_commands[i].str, (char *)command.buf,
+ MIN(clen, command.used))) {
+ gdb_commands[i].handler(&command, clen, &reply);
+ break;
+ }
+ }
+
+ /* If we're resuming, we won't send a reply until we stop. */
+ if (gdb_state.resumed)
+ return;
+
+ gdb_send_reply(&reply);
+ }
+}
+
+static void gdb_output_write(const void *buffer, size_t count)
+{
+ if (!gdb_state.resumed) {
+ /* Must be a die_if() in GDB (or a bug), so bail out and die. */
+ gdb_exit(-1);
+ video_console_init();
+ puts("GDB died, redirecting its last words to the screen:\n");
+ console_write(buffer, count);
+ } else {
+ reply.used = 0;
+ reply.buf[reply.used++] = 'O';
+ gdb_message_encode_bytes(&reply, buffer, count);
+ gdb_send_reply(&reply);
+ }
+}
+
+static struct console_output_driver gdb_output_driver = {
+ .write = &gdb_output_write
+};
+
+static void gdb_init(void)
+{
+ printf("Ready for GDB connection.\n");
+ gdb_transport_init();
+ gdb_arch_init();
+ console_add_output_driver(&gdb_output_driver);
+}
+
+void gdb_enter(void)
+{
+ if (!gdb_state.connected)
+ gdb_init();
+ gdb_arch_enter();
+}
+
+void gdb_exit(s8 exit_status)
+{
+ if (!gdb_state.connected)
+ return;
+
+ reply.used = 0;
+ gdb_message_add_string(&reply, "W");
+ gdb_message_encode_bytes(&reply, &exit_status, 1);
+ gdb_send_reply(&reply);
+
+ console_remove_output_driver(&gdb_output_write);
+ gdb_transport_teardown();
+ gdb_state.connected = 0;
+ printf("Detached from GDB connection.\n");
+}