* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "config.h"
-#include "qemu-common.h"
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/cutils.h"
+#include "cpu.h"
#ifdef CONFIG_USER_ONLY
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-
#include "qemu.h"
#else
#include "monitor/monitor.h"
#include "cpu.h"
#include "qemu/sockets.h"
#include "sysemu/kvm.h"
+#include "exec/semihost.h"
+#include "exec/exec-all.h"
+
+#ifdef CONFIG_USER_ONLY
+#define GDB_ATTACHED "0"
+#else
+#define GDB_ATTACHED "1"
+#endif
static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr,
uint8_t *buf, int len, bool is_write)
int fd;
int running_state;
#else
- CharDriverState *chr;
+ CharBackend chr;
CharDriverState *mon_chr;
#endif
char syscall_buf[256];
if (ret < 0) {
if (errno == ECONNRESET)
s->fd = -1;
- if (errno != EINTR && errno != EAGAIN)
+ if (errno != EINTR)
return -1;
} else if (ret == 0) {
close(s->fd);
GDB_SYS_DISABLED,
} gdb_syscall_mode;
-/* If gdb is connected when the first semihosting syscall occurs then use
- remote gdb syscalls. Otherwise use native file IO. */
+/* Decide if either remote gdb syscalls or native file IO should be used. */
int use_gdb_syscalls(void)
{
+ SemihostingTarget target = semihosting_get_target();
+ if (target == SEMIHOSTING_TARGET_NATIVE) {
+ /* -semihosting-config target=native */
+ return false;
+ } else if (target == SEMIHOSTING_TARGET_GDB) {
+ /* -semihosting-config target=gdb */
+ return true;
+ }
+
+ /* -semihosting-config target=auto */
+ /* On the first call check if gdb is connected and remember. */
if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED
: GDB_SYS_DISABLED);
while (len > 0) {
ret = send(s->fd, buf, len, 0);
if (ret < 0) {
- if (errno != EINTR && errno != EAGAIN)
+ if (errno != EINTR)
return;
} else {
buf += ret;
}
}
#else
- qemu_chr_fe_write(s->chr, buf, len);
+ /* XXX this blocks entire thread. Rewrite to use
+ * qemu_chr_fe_write and background I/O callbacks */
+ qemu_chr_fe_write_all(&s->chr, buf, len);
#endif
}
GDBRegisterState *r;
CPUState *cpu = first_cpu;
- snprintf(target_xml, sizeof(target_xml),
- "<?xml version=\"1.0\"?>"
- "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
- "<target>"
- "<xi:include href=\"%s\"/>",
- cc->gdb_core_xml_file);
-
+ pstrcat(target_xml, sizeof(target_xml),
+ "<?xml version=\"1.0\"?>"
+ "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
+ "<target>");
+ if (cc->gdb_arch_name) {
+ gchar *arch = cc->gdb_arch_name(cpu);
+ pstrcat(target_xml, sizeof(target_xml), "<architecture>");
+ pstrcat(target_xml, sizeof(target_xml), arch);
+ pstrcat(target_xml, sizeof(target_xml), "</architecture>");
+ g_free(arch);
+ }
+ pstrcat(target_xml, sizeof(target_xml), "<xi:include href=\"");
+ pstrcat(target_xml, sizeof(target_xml), cc->gdb_core_xml_file);
+ pstrcat(target_xml, sizeof(target_xml), "\"/>");
for (r = cpu->gdb_regs; r; r = r->next) {
pstrcat(target_xml, sizeof(target_xml), "<xi:include href=\"");
pstrcat(target_xml, sizeof(target_xml), r->xml);
static void gdb_set_cpu_pc(GDBState *s, target_ulong pc)
{
CPUState *cpu = s->c_cpu;
- CPUClass *cc = CPU_GET_CLASS(cpu);
cpu_synchronize_state(cpu);
- if (cc->set_pc) {
- cc->set_pc(cpu, pc);
- }
+ cpu_set_pc(cpu, pc);
}
static CPUState *find_cpu(uint32_t thread_id)
return NULL;
}
+static int is_query_packet(const char *p, const char *query, char separator)
+{
+ unsigned int query_len = strlen(query);
+
+ return strncmp(p, query, query_len) == 0 &&
+ (p[query_len] == '\0' || p[query_len] == separator);
+}
+
static int gdb_handle_packet(GDBState *s, const char *line_buf)
{
CPUState *cpu;
action = *p++;
signal = 0;
if (action == 'C' || action == 'S') {
- signal = strtoul(p, (char **)&p, 16);
+ signal = gdb_signal_to_target(strtoul(p, (char **)&p, 16));
+ if (signal == -1) {
+ signal = 0;
+ }
} else if (action != 'c' && action != 's') {
res = 0;
break;
goto unknown_command;
}
case 'k':
-#ifdef CONFIG_USER_ONLY
/* Kill the target */
fprintf(stderr, "\nQEMU: Terminated via GDBstub\n");
exit(0);
-#endif
case 'D':
/* Detach packet */
gdb_breakpoint_remove_all();
if (*p == ',')
p++;
len = strtoull(p, NULL, 16);
+
+ /* memtohex() doubles the required space */
+ if (len > MAX_PACKET_LENGTH / 2) {
+ put_packet (s, "E22");
+ break;
+ }
+
if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, false) != 0) {
put_packet (s, "E14");
} else {
len = strtoull(p, (char **)&p, 16);
if (*p == ':')
p++;
+
+ /* hextomem() reads 2*len bytes */
+ if (len > strlen(p) / 2) {
+ put_packet (s, "E22");
+ break;
+ }
hextomem(mem_buf, p, len);
if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len,
true) != 0) {
SSTEP_NOTIMER);
put_packet(s, buf);
break;
- } else if (strncmp(p,"qemu.sstep",10) == 0) {
+ } else if (is_query_packet(p, "qemu.sstep", '=')) {
/* Display or change the sstep_flags */
p += 10;
if (*p != '=') {
cpu = find_cpu(thread);
if (cpu != NULL) {
cpu_synchronize_state(cpu);
- len = snprintf((char *)mem_buf, sizeof(mem_buf),
+ /* memtohex() doubles the required space */
+ len = snprintf((char *)mem_buf, sizeof(buf) / 2,
"CPU#%d [%s]", cpu->cpu_index,
cpu->halted ? "halted " : "running");
memtohex(buf, mem_buf, len);
break;
}
#ifdef CONFIG_USER_ONLY
- else if (strncmp(p, "Offsets", 7) == 0) {
+ else if (strcmp(p, "Offsets") == 0) {
TaskState *ts = s->c_cpu->opaque;
snprintf(buf, sizeof(buf),
put_packet(s, "E01");
break;
}
- hextomem(mem_buf, p + 5, len);
len = len / 2;
+ hextomem(mem_buf, p + 5, len);
mem_buf[len++] = 0;
qemu_chr_be_write(s->mon_chr, mem_buf, len);
put_packet(s, "OK");
break;
}
#endif /* !CONFIG_USER_ONLY */
- if (strncmp(p, "Supported", 9) == 0) {
+ if (is_query_packet(p, "Supported", ':')) {
snprintf(buf, sizeof(buf), "PacketSize=%x", MAX_PACKET_LENGTH);
cc = CPU_GET_CLASS(first_cpu);
if (cc->gdb_core_xml_file != NULL) {
put_packet_binary(s, buf, len + 1);
break;
}
+ if (is_query_packet(p, "Attached", ':')) {
+ put_packet(s, GDB_ATTACHED);
+ break;
+ }
/* Unrecognised 'q' command. */
goto unknown_command;
static void gdb_vm_state_change(void *opaque, int running, RunState state)
{
GDBState *s = gdbserver_state;
- CPUArchState *env = s->c_cpu->env_ptr;
CPUState *cpu = s->c_cpu;
char buf[256];
const char *type;
cpu->watchpoint_hit = NULL;
goto send_packet;
}
- tb_flush(env);
+ tb_flush(cpu);
ret = GDB_SIGNAL_TRAP;
break;
case RUN_STATE_PAUSED:
ret = GDB_SIGNAL_UNKNOWN;
break;
}
+ gdb_set_stop_cpu(cpu);
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(cpu));
send_packet:
%x - target_ulong argument printed in hex.
%lx - 64-bit argument printed in hex.
%s - string pointer (target_ulong) and length (int) pair. */
-void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
+void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va)
{
- va_list va;
char *p;
char *p_end;
target_ulong addr;
#ifndef CONFIG_USER_ONLY
vm_stop(RUN_STATE_DEBUG);
#endif
- va_start(va, fmt);
p = s->syscall_buf;
p_end = &s->syscall_buf[sizeof(s->syscall_buf)];
*(p++) = 'F';
}
}
*p = 0;
- va_end(va);
#ifdef CONFIG_USER_ONLY
put_packet(s, s->syscall_buf);
gdb_handlesig(s->c_cpu, 0);
is still in the running state, which can cause packets to be dropped
and state transition 'T' packets to be sent while the syscall is still
being processed. */
- cpu_exit(s->c_cpu);
+ qemu_cpu_kick(s->c_cpu);
#endif
}
+void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ gdb_do_syscallv(cb, fmt, va);
+ va_end(va);
+}
+
static void gdb_read_byte(GDBState *s, int ch)
{
int i, csum;
{
GDBState *s;
char buf[4];
+#ifndef CONFIG_USER_ONLY
+ CharDriverState *chr;
+#endif
s = gdbserver_state;
if (!s) {
if (gdbserver_fd < 0 || s->fd < 0) {
return;
}
+#else
+ chr = qemu_chr_fe_get_driver(&s->chr);
+ if (!chr) {
+ return;
+ }
#endif
snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
put_packet(s, buf);
#ifndef CONFIG_USER_ONLY
- if (s->chr) {
- qemu_chr_delete(s->chr);
- }
+ qemu_chr_fe_deinit(&s->chr);
+ qemu_chr_delete(chr);
#endif
}
#ifdef CONFIG_USER_ONLY
-int
-gdb_queuesig (void)
-{
- GDBState *s;
-
- s = gdbserver_state;
-
- if (gdbserver_fd < 0 || s->fd < 0)
- return 0;
- else
- return 1;
-}
-
int
gdb_handlesig(CPUState *cpu, int sig)
{
- CPUArchState *env = cpu->env_ptr;
GDBState *s;
char buf[256];
int n;
/* disable single step if it was enabled */
cpu_single_step(cpu, 0);
- tb_flush(env);
+ tb_flush(cpu);
if (sig != 0) {
snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb(sig));
for (i = 0; i < n; i++) {
gdb_read_byte(s, buf[i]);
}
- } else if (n == 0 || errno != EAGAIN) {
+ } else {
/* XXX: Connection closed. Should probably wait for another
connection before continuing. */
+ if (n == 0) {
+ close(s->fd);
+ }
+ s->fd = -1;
return sig;
}
}
gdb_has_xml = false;
gdbserver_state = s;
-
- fcntl(fd, F_SETFL, O_NONBLOCK);
}
static int gdbserver_open(int port)
close(fd);
return -1;
}
- ret = listen(fd, 0);
+ ret = listen(fd, 1);
if (ret < 0) {
perror("listen");
close(fd);
}
/* Disable gdb stub for child processes. */
-void gdbserver_fork(CPUArchState *env)
+void gdbserver_fork(CPUState *cpu)
{
- CPUState *cpu = ENV_GET_CPU(env);
GDBState *s = gdbserver_state;
if (gdbserver_fd < 0 || s->fd < 0) {
char gdbstub_device_name[128];
CharDriverState *chr = NULL;
CharDriverState *mon_chr;
+ ChardevCommon common = { 0 };
if (!device)
return -1;
sigaction(SIGINT, &act, NULL);
}
#endif
- chr = qemu_chr_new("gdb", device, NULL);
+ chr = qemu_chr_new_noreplay("gdb", device);
if (!chr)
return -1;
-
- qemu_chr_fe_claim_no_fail(chr);
- qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive,
- gdb_chr_event, NULL);
}
s = gdbserver_state;
qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
/* Initialize a monitor terminal for gdb */
- mon_chr = qemu_chr_alloc();
+ mon_chr = qemu_chr_alloc(&common, &error_abort);
mon_chr->chr_write = gdb_monitor_write;
monitor_init(mon_chr, 0);
} else {
- if (s->chr)
- qemu_chr_delete(s->chr);
+ if (qemu_chr_fe_get_driver(&s->chr)) {
+ qemu_chr_delete(qemu_chr_fe_get_driver(&s->chr));
+ }
mon_chr = s->mon_chr;
memset(s, 0, sizeof(GDBState));
+ s->mon_chr = mon_chr;
}
s->c_cpu = first_cpu;
s->g_cpu = first_cpu;
- s->chr = chr;
+ if (chr) {
+ qemu_chr_fe_init(&s->chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive,
+ gdb_chr_event, NULL, NULL, true);
+ }
s->state = chr ? RS_IDLE : RS_INACTIVE;
s->mon_chr = mon_chr;
s->current_syscall_cb = NULL;