Newer
Older
* This implements a subset of the remote protocol as described in:
*
* https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* 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/>.
*
* SPDX-License-Identifier: LGPL-2.0+
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/ctype.h"
#include "qemu/module.h"
#include "trace/trace-root.h"
#include "monitor/monitor.h"
#include "chardev/char-fe.h"
#include "hw/boards.h"
#include "sysemu/runstate.h"
#include "semihosting/semihost.h"
#include "sysemu/replay.h"
#ifdef CONFIG_USER_ONLY
#define GDB_ATTACHED "0"
#else
#define GDB_ATTACHED "1"
#endif
#ifndef CONFIG_USER_ONLY
static int phy_memory_mode;
#endif
static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr,
uint8_t *buf, int len, bool is_write)
#ifndef CONFIG_USER_ONLY
if (phy_memory_mode) {
if (is_write) {
cpu_physical_memory_write(addr, buf, len);
} else {
cpu_physical_memory_read(addr, buf, len);
}
return 0;
}
#endif
cc = CPU_GET_CLASS(cpu);
if (cc->memory_rw_debug) {
return cc->memory_rw_debug(cpu, addr, buf, len, is_write);
}
return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
/* Return the GDB index for a given vCPU state.
*
* For user mode this is simply the thread id. In system mode GDB
* numbers CPUs from 1 as 0 is reserved as an "any cpu" index.
*/
static inline int cpu_gdb_index(CPUState *cpu)
{
#if defined(CONFIG_USER_ONLY)
return ts ? ts->ts_tid : -1;
#else
return cpu->cpu_index + 1;
#endif
}
enum {
GDB_SIGNAL_0 = 0,
GDB_SIGNAL_INT = 2,
GDB_SIGNAL_ABRT = 6,
GDB_SIGNAL_ALRM = 14,
GDB_SIGNAL_IO = 23,
GDB_SIGNAL_XCPU = 24,
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
GDB_SIGNAL_UNKNOWN = 143
};
#ifdef CONFIG_USER_ONLY
/* Map target signal numbers to GDB protocol signal numbers and vice
* versa. For user emulation's currently supported systems, we can
* assume most signals are defined.
*/
static int gdb_signal_table[] = {
0,
TARGET_SIGHUP,
TARGET_SIGINT,
TARGET_SIGQUIT,
TARGET_SIGILL,
TARGET_SIGTRAP,
TARGET_SIGABRT,
-1, /* SIGEMT */
TARGET_SIGFPE,
TARGET_SIGKILL,
TARGET_SIGBUS,
TARGET_SIGSEGV,
TARGET_SIGSYS,
TARGET_SIGPIPE,
TARGET_SIGALRM,
TARGET_SIGTERM,
TARGET_SIGURG,
TARGET_SIGSTOP,
TARGET_SIGTSTP,
TARGET_SIGCONT,
TARGET_SIGCHLD,
TARGET_SIGTTIN,
TARGET_SIGTTOU,
TARGET_SIGIO,
TARGET_SIGXCPU,
TARGET_SIGXFSZ,
TARGET_SIGVTALRM,
TARGET_SIGPROF,
TARGET_SIGWINCH,
-1, /* SIGLOST */
TARGET_SIGUSR1,
TARGET_SIGUSR2,
-1, /* SIGPOLL */
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
__SIGRTMIN + 1,
__SIGRTMIN + 2,
__SIGRTMIN + 3,
__SIGRTMIN + 4,
__SIGRTMIN + 5,
__SIGRTMIN + 6,
__SIGRTMIN + 7,
__SIGRTMIN + 8,
__SIGRTMIN + 9,
__SIGRTMIN + 10,
__SIGRTMIN + 11,
__SIGRTMIN + 12,
__SIGRTMIN + 13,
__SIGRTMIN + 14,
__SIGRTMIN + 15,
__SIGRTMIN + 16,
__SIGRTMIN + 17,
__SIGRTMIN + 18,
__SIGRTMIN + 19,
__SIGRTMIN + 20,
__SIGRTMIN + 21,
__SIGRTMIN + 22,
__SIGRTMIN + 23,
__SIGRTMIN + 24,
__SIGRTMIN + 25,
__SIGRTMIN + 26,
__SIGRTMIN + 27,
__SIGRTMIN + 28,
__SIGRTMIN + 29,
__SIGRTMIN + 30,
__SIGRTMIN + 31,
-1, /* SIGCANCEL */
__SIGRTMIN,
__SIGRTMIN + 32,
__SIGRTMIN + 33,
__SIGRTMIN + 34,
__SIGRTMIN + 35,
__SIGRTMIN + 36,
__SIGRTMIN + 37,
__SIGRTMIN + 38,
__SIGRTMIN + 39,
__SIGRTMIN + 40,
__SIGRTMIN + 41,
__SIGRTMIN + 42,
__SIGRTMIN + 43,
__SIGRTMIN + 44,
__SIGRTMIN + 45,
__SIGRTMIN + 46,
__SIGRTMIN + 47,
__SIGRTMIN + 48,
__SIGRTMIN + 49,
__SIGRTMIN + 50,
__SIGRTMIN + 51,
__SIGRTMIN + 52,
__SIGRTMIN + 53,
__SIGRTMIN + 54,
__SIGRTMIN + 55,
__SIGRTMIN + 56,
__SIGRTMIN + 57,
__SIGRTMIN + 58,
__SIGRTMIN + 59,
__SIGRTMIN + 60,
__SIGRTMIN + 61,
__SIGRTMIN + 62,
__SIGRTMIN + 63,
__SIGRTMIN + 64,
__SIGRTMIN + 65,
__SIGRTMIN + 66,
__SIGRTMIN + 67,
__SIGRTMIN + 68,
__SIGRTMIN + 69,
__SIGRTMIN + 70,
__SIGRTMIN + 71,
__SIGRTMIN + 72,
__SIGRTMIN + 73,
__SIGRTMIN + 74,
__SIGRTMIN + 75,
__SIGRTMIN + 76,
__SIGRTMIN + 77,
__SIGRTMIN + 78,
__SIGRTMIN + 79,
__SIGRTMIN + 80,
__SIGRTMIN + 81,
__SIGRTMIN + 82,
__SIGRTMIN + 83,
__SIGRTMIN + 84,
__SIGRTMIN + 85,
__SIGRTMIN + 86,
__SIGRTMIN + 87,
__SIGRTMIN + 88,
__SIGRTMIN + 89,
__SIGRTMIN + 90,
__SIGRTMIN + 91,
__SIGRTMIN + 92,
__SIGRTMIN + 93,
__SIGRTMIN + 94,
__SIGRTMIN + 95,
-1, /* SIGINFO */
-1, /* UNKNOWN */
-1, /* DEFAULT */
-1,
-1,
-1,
-1,
-1,
-1
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
/* In system mode we only need SIGINT and SIGTRAP; other signals
are not yet supported. */
enum {
TARGET_SIGINT = 2,
TARGET_SIGTRAP = 5
};
static int gdb_signal_table[] = {
-1,
-1,
TARGET_SIGINT,
-1,
-1,
TARGET_SIGTRAP
};
#endif
#ifdef CONFIG_USER_ONLY
static int target_signal_to_gdb (int sig)
{
int i;
for (i = 0; i < ARRAY_SIZE (gdb_signal_table); i++)
if (gdb_signal_table[i] == sig)
return i;
return GDB_SIGNAL_UNKNOWN;
}
static int gdb_signal_to_target (int sig)
{
if (sig < ARRAY_SIZE (gdb_signal_table))
return gdb_signal_table[sig];
else
return -1;
}
typedef struct GDBRegisterState {
int base_reg;
int num_regs;
gdb_get_reg_cb get_reg;
gdb_set_reg_cb set_reg;
const char *xml;
struct GDBRegisterState *next;
} GDBRegisterState;
typedef struct GDBProcess {
uint32_t pid;
bool attached;
char target_xml[1024];
RS_IDLE,
RS_GETLINE,
RS_GETLINE_ESC,
RS_GETLINE_RLE,
RS_CHKSUM1,
RS_CHKSUM2,
};
typedef struct GDBState {
bool init; /* have we been initialised? */
CPUState *c_cpu; /* current CPU for step/continue ops */
CPUState *g_cpu; /* current CPU for other ops */
CPUState *query_cpu; /* for q{f|s}ThreadInfo */
char line_buf[MAX_PACKET_LENGTH];
int line_sum; /* running checksum */
int line_csum; /* checksum at the end of the packet */
GByteArray *last_packet;

Edgar E. Iglesias
committed
int signal;
char *socket_path;
bool multiprocess;
GDBProcess *processes;
int process_num;
char syscall_buf[256];
gdb_syscall_complete_cb current_syscall_cb;
int sstep_flags;
int supported_sstep_flags;
static GDBState gdbserver_state;
static void init_gdbserver_state(void)
{
g_assert(!gdbserver_state.init);
memset(&gdbserver_state, 0, sizeof(GDBState));
gdbserver_state.init = true;
gdbserver_state.str_buf = g_string_new(NULL);
gdbserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH);
gdbserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4);
/*
* In replay mode all events will come from the log and can't be
* suppressed otherwise we would break determinism. However as those
* events are tied to the number of executed instructions we won't see
* them occurring every time we single step.
*/
if (replay_mode != REPLAY_MODE_NONE) {
gdbserver_state.supported_sstep_flags = SSTEP_ENABLE;
} else if (kvm_enabled()) {
gdbserver_state.supported_sstep_flags = kvm_get_supported_sstep_flags();
} else {
gdbserver_state.supported_sstep_flags =
SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER;
}
/*
* By default use no IRQs and no timers while single stepping so as to
* make single stepping like an ICE HW step.
*/
gdbserver_state.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER;
gdbserver_state.sstep_flags &= gdbserver_state.supported_sstep_flags;
}
#ifndef CONFIG_USER_ONLY
static void reset_gdbserver_state(void)
{
g_free(gdbserver_state.processes);
gdbserver_state.processes = NULL;
gdbserver_state.process_num = 0;
}
#endif
static int get_char(void)
{
uint8_t ch;
int ret;
for(;;) {
ret = qemu_recv(gdbserver_state.fd, &ch, 1, 0);

Edgar E. Iglesias
committed
if (errno == ECONNRESET)
gdbserver_state.fd = -1;
close(gdbserver_state.fd);
gdbserver_state.fd = -1;
return -1;
} else {
break;
}
}
return ch;
}
GDB_SYS_UNKNOWN,
GDB_SYS_ENABLED,
GDB_SYS_DISABLED,
} gdb_syscall_mode;
/* Decide if either remote gdb syscalls or native file IO should be used. */
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. */
gdb_syscall_mode = gdbserver_state.init ?
GDB_SYS_ENABLED : GDB_SYS_DISABLED;
}
return gdb_syscall_mode == GDB_SYS_ENABLED;
}
static bool stub_can_reverse(void)
{
#ifdef CONFIG_USER_ONLY
return false;
#else
return replay_mode == REPLAY_MODE_PLAY;
#endif
}

Edgar E. Iglesias
committed
/* Resume execution. */
static inline void gdb_continue(void)

Edgar E. Iglesias
committed
{

Edgar E. Iglesias
committed
#ifdef CONFIG_USER_ONLY
gdbserver_state.running_state = 1;

Edgar E. Iglesias
committed
#else
if (!runstate_needs_reset()) {

Edgar E. Iglesias
committed
#endif
}
/*
* Resume execution, per CPU actions. For user-mode emulation it's
* equivalent to gdb_continue.
*/
static int gdb_continue_partial(char *newstates)
{
CPUState *cpu;
int res = 0;
#ifdef CONFIG_USER_ONLY
/*
* This is not exactly accurate, but it's an improvement compared to the
* previous situation, where only one CPU would be single-stepped.
*/
CPU_FOREACH(cpu) {
if (newstates[cpu->cpu_index] == 's') {
cpu_single_step(cpu, gdbserver_state.sstep_flags);
gdbserver_state.running_state = 1;
#else
int flag = 0;
if (!runstate_needs_reset()) {
if (vm_prepare_start()) {
return 0;
}
CPU_FOREACH(cpu) {
switch (newstates[cpu->cpu_index]) {
case 0:
case 1:
break; /* nothing to do here */
case 's':
cpu_single_step(cpu, gdbserver_state.sstep_flags);
cpu_resume(cpu);
flag = 1;
break;
case 'c':
cpu_resume(cpu);
flag = 1;
break;
default:
res = -1;
break;
}
}
}
if (flag) {
qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
}
#endif
return res;
}
static void put_buffer(const uint8_t *buf, int len)
#ifdef CONFIG_USER_ONLY
ret = send(gdbserver_state.fd, buf, len, 0);
return;
} else {
buf += ret;
len -= ret;
}
}
/* XXX this blocks entire thread. Rewrite to use
* qemu_chr_fe_write and background I/O callbacks */
qemu_chr_fe_write_all(&gdbserver_state.chr, buf, len);
}
static inline int fromhex(int v)
{
if (v >= '0' && v <= '9')
return v - '0';
else if (v >= 'A' && v <= 'F')
return v - 'A' + 10;
else if (v >= 'a' && v <= 'f')
return v - 'a' + 10;
else
return 0;
}
static inline int tohex(int v)
{
if (v < 10)
return v + '0';
else
return v - 10 + 'a';
}
/* writes 2*len+1 bytes in buf */
static void memtohex(GString *buf, const uint8_t *mem, int len)
{
int i, c;
for(i = 0; i < len; i++) {
c = mem[i];
g_string_append_c(buf, tohex(c >> 4));
g_string_append_c(buf, tohex(c & 0xf));
g_string_append_c(buf, '\0');
static void hextomem(GByteArray *mem, const char *buf, int len)
{
int i;
for(i = 0; i < len; i++) {
guint8 byte = fromhex(buf[0]) << 4 | fromhex(buf[1]);
g_byte_array_append(mem, &byte, 1);
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
static void hexdump(const char *buf, int len,
void (*trace_fn)(size_t ofs, char const *text))
{
char line_buffer[3 * 16 + 4 + 16 + 1];
size_t i;
for (i = 0; i < len || (i & 0xF); ++i) {
size_t byte_ofs = i & 15;
if (byte_ofs == 0) {
memset(line_buffer, ' ', 3 * 16 + 4 + 16);
line_buffer[3 * 16 + 4 + 16] = 0;
}
size_t col_group = (i >> 2) & 3;
size_t hex_col = byte_ofs * 3 + col_group;
size_t txt_col = 3 * 16 + 4 + byte_ofs;
if (i < len) {
char value = buf[i];
line_buffer[hex_col + 0] = tohex((value >> 4) & 0xF);
line_buffer[hex_col + 1] = tohex((value >> 0) & 0xF);
line_buffer[txt_col + 0] = (value >= ' ' && value < 127)
? value
: '.';
}
if (byte_ofs == 0xF)
trace_fn(i & -16, line_buffer);
}
}
static int put_packet_binary(const char *buf, int len, bool dump)
if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) {
hexdump(buf, len, trace_gdbstub_io_binaryreply);
}
g_byte_array_set_size(gdbserver_state.last_packet, 0);
g_byte_array_append(gdbserver_state.last_packet,
(const uint8_t *) "$", 1);
g_byte_array_append(gdbserver_state.last_packet,
(const uint8_t *) buf, len);
csum = 0;
for(i = 0; i < len; i++) {
csum += buf[i];
}
footer[0] = '#';
footer[1] = tohex((csum >> 4) & 0xf);
footer[2] = tohex((csum) & 0xf);
g_byte_array_append(gdbserver_state.last_packet, footer, 3);
put_buffer(gdbserver_state.last_packet->data,
gdbserver_state.last_packet->len);
#ifdef CONFIG_USER_ONLY
#else
break;
#endif
/* return -1 if error, 0 if OK */
static int put_packet(const char *buf)
return put_packet_binary(buf, strlen(buf), false);
static void put_strbuf(void)
{
put_packet(gdbserver_state.str_buf->str);
}
/* Encode data using the encoding for 'x' packets. */
static void memtox(GString *buf, const char *mem, int len)
{
char c;
while (len--) {
c = *(mem++);
switch (c) {
case '#': case '$': case '*': case '}':
g_string_append_c(buf, '}');
g_string_append_c(buf, c ^ 0x20);
g_string_append_c(buf, c);
static uint32_t gdb_get_cpu_pid(CPUState *cpu)
/* TODO: In user mode, we should use the task state PID */
if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
/* Return the default process' PID */
int index = gdbserver_state.process_num - 1;
return gdbserver_state.processes[index].pid;
return cpu->cluster_index + 1;
static GDBProcess *gdb_get_process(uint32_t pid)
{
int i;
if (!pid) {
/* 0 means any process, we take the first one */
return &gdbserver_state.processes[0];
for (i = 0; i < gdbserver_state.process_num; i++) {
if (gdbserver_state.processes[i].pid == pid) {
return &gdbserver_state.processes[i];
}
}
return NULL;
}
static GDBProcess *gdb_get_cpu_process(CPUState *cpu)
return gdb_get_process(gdb_get_cpu_pid(cpu));
}
static CPUState *find_cpu(uint32_t thread_id)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
if (cpu_gdb_index(cpu) == thread_id) {
return cpu;
}
}
return NULL;
}
static CPUState *get_first_cpu_in_process(GDBProcess *process)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
if (gdb_get_cpu_pid(cpu) == process->pid) {
return cpu;
}
}
return NULL;
}
static CPUState *gdb_next_cpu_in_process(CPUState *cpu)
uint32_t pid = gdb_get_cpu_pid(cpu);
cpu = CPU_NEXT(cpu);
while (cpu) {
if (gdb_get_cpu_pid(cpu) == pid) {
break;
}
cpu = CPU_NEXT(cpu);
}
return cpu;
}
/* Return the cpu following @cpu, while ignoring unattached processes. */
static CPUState *gdb_next_attached_cpu(CPUState *cpu)
{
cpu = CPU_NEXT(cpu);
while (cpu) {
if (gdb_get_cpu_process(cpu)->attached) {
break;
}
cpu = CPU_NEXT(cpu);
}
return cpu;
}
/* Return the first attached cpu */
static CPUState *gdb_first_attached_cpu(void)
{
CPUState *cpu = first_cpu;
GDBProcess *process = gdb_get_cpu_process(cpu);
if (!process->attached) {
return gdb_next_attached_cpu(cpu);
}
return cpu;
}
static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid)
{
GDBProcess *process;
CPUState *cpu;
if (!pid && !tid) {
/* 0 means any process/thread, we take the first attached one */
return gdb_first_attached_cpu();
} else if (pid && !tid) {
/* any thread in a specific process */
process = gdb_get_process(pid);
if (process == NULL) {
return NULL;
}
if (!process->attached) {
return NULL;
}
return get_first_cpu_in_process(process);
} else {
/* a specific thread */
cpu = find_cpu(tid);
if (cpu == NULL) {
return NULL;
}
process = gdb_get_cpu_process(cpu);
if (pid && process->pid != pid) {
return NULL;
}
if (!process->attached) {
return NULL;
}
return cpu;
}
}
static const char *get_feature_xml(const char *p, const char **newp,
GDBProcess *process)
{
size_t len;
int i;
const char *name;
CPUState *cpu = get_first_cpu_in_process(process);
CPUClass *cc = CPU_GET_CLASS(cpu);
len = 0;
while (p[len] && p[len] != ':')
len++;
*newp = p + len;
name = NULL;
if (strncmp(p, "target.xml", len) == 0) {
char *buf = process->target_xml;
const size_t buf_sz = sizeof(process->target_xml);
/* Generate the XML description for this CPU. */
pstrcat(buf, buf_sz,
"<?xml version=\"1.0\"?>"
"<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
"<target>");
if (cc->gdb_arch_name) {
gchar *arch = cc->gdb_arch_name(cpu);
pstrcat(buf, buf_sz, "<architecture>");
pstrcat(buf, buf_sz, arch);
pstrcat(buf, buf_sz, "</architecture>");
g_free(arch);
}
pstrcat(buf, buf_sz, "<xi:include href=\"");
pstrcat(buf, buf_sz, cc->gdb_core_xml_file);
pstrcat(buf, buf_sz, "\"/>");
for (r = cpu->gdb_regs; r; r = r->next) {
pstrcat(buf, buf_sz, "<xi:include href=\"");
pstrcat(buf, buf_sz, r->xml);
pstrcat(buf, buf_sz, "\"/>");
pstrcat(buf, buf_sz, "</target>");
if (cc->gdb_get_dynamic_xml) {
char *xmlname = g_strndup(p, len);
const char *xml = cc->gdb_get_dynamic_xml(cpu, xmlname);
g_free(xmlname);
if (xml) {
return xml;
}
}
for (i = 0; ; i++) {
name = xml_builtin[i][0];
if (!name || (strncmp(name, p, len) == 0 && strlen(name) == len))
break;
}
return name ? xml_builtin[i][1] : NULL;
}
static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg)
CPUClass *cc = CPU_GET_CLASS(cpu);
CPUArchState *env = cpu->env_ptr;
if (reg < cc->gdb_num_core_regs) {
return cc->gdb_read_register(cpu, buf, reg);
}
for (r = cpu->gdb_regs; r; r = r->next) {
if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) {
return r->get_reg(env, buf, reg - r->base_reg);
static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg)
CPUClass *cc = CPU_GET_CLASS(cpu);
CPUArchState *env = cpu->env_ptr;
if (reg < cc->gdb_num_core_regs) {
return cc->gdb_write_register(cpu, mem_buf, reg);
}
for (r = cpu->gdb_regs; r; r = r->next) {
if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) {
return r->set_reg(env, mem_buf, reg - r->base_reg);
}
}
/* Register a supplemental set of CPU registers. If g_pos is nonzero it
specifies the first register number and these registers are included in
a standard "g" packet. Direction is relative to gdb, i.e. get_reg is
gdb reading a CPU register, and set_reg is gdb modifying a CPU register.
*/
void gdb_register_coprocessor(CPUState *cpu,
gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
int num_regs, const char *xml, int g_pos)
GDBRegisterState *s;
GDBRegisterState **p;
p = &cpu->gdb_regs;
while (*p) {
/* Check for duplicates. */
if (strcmp((*p)->xml, xml) == 0)
return;
p = &(*p)->next;
}
s->base_reg = cpu->gdb_num_regs;
s->num_regs = num_regs;
s->get_reg = get_reg;
s->set_reg = set_reg;
s->xml = xml;