/* * 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. */ #include <arch/cache.h> #include <gdb.h> #include <libpayload.h> static void gdb_get_last_signal(struct gdb_message *command, int offset, struct gdb_message *reply) { gdb_message_add_string(reply, "S"); gdb_message_encode_bytes(reply, &gdb_state.signal, 1); } static void gdb_read_general_registers(struct gdb_message *command, int offset, struct gdb_message *reply) { gdb_arch_encode_regs(reply); } static void gdb_write_general_registers(struct gdb_message *command, int offset, struct gdb_message *reply) { gdb_arch_decode_regs(offset, command); gdb_message_add_string(reply, "OK"); } static void gdb_read_memory(struct gdb_message *command, int offset, struct gdb_message *reply) { int tok = gdb_message_tokenize(command, &offset); uintptr_t addr = gdb_message_decode_int(command, tok, offset - 1 - tok); size_t length = gdb_message_decode_int(command, offset, command->used - offset); gdb_message_encode_bytes(reply, (void *)addr, length); } static void gdb_write_memory(struct gdb_message *command, int offset, struct gdb_message *reply) { int tok = gdb_message_tokenize(command, &offset); uintptr_t addr = gdb_message_decode_int(command, tok, offset - 1 - tok); tok = gdb_message_tokenize(command, &offset); size_t length = gdb_message_decode_int(command, tok, offset - 1 - tok); die_if(length * 2 != command->used - offset, "Invalid length field in " "GDB command: %.*s", command->used, command->buf); gdb_message_decode_bytes(command, offset, (void *)addr, length); cache_sync_instructions(); gdb_message_add_string(reply, "OK"); } static void gdb_continue(struct gdb_message *command, int offset, struct gdb_message *reply) { /* Disable single step if it's still on. */ gdb_arch_set_single_step(0); /* No need to support the extension that passes in new EIP/PC. */ if (command->used > offset) gdb_message_add_string(reply, "E00"); else gdb_state.resumed = 1; } static void gdb_single_step(struct gdb_message *command, int offset, struct gdb_message *reply) { if (command->used > offset || gdb_arch_set_single_step(1)) gdb_message_add_string(reply, "E00"); else gdb_state.resumed = 1; } struct gdb_command gdb_commands[] = { { "?", &gdb_get_last_signal }, { "g", &gdb_read_general_registers }, { "G", &gdb_write_general_registers }, { "m", &gdb_read_memory }, { "M", &gdb_write_memory }, { "c", &gdb_continue }, { "s", &gdb_single_step } }; const int gdb_command_count = ARRAY_SIZE(gdb_commands);