]> git.proxmox.com Git - mirror_qemu.git/blame - gdbstub.c
real mode support
[mirror_qemu.git] / gdbstub.c
CommitLineData
b4608c04
FB
1/*
2 * gdb server stub
3 *
4 * Copyright (c) 2003 Fabrice Bellard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23#include <unistd.h>
24#include <errno.h>
25#include <sys/socket.h>
26#include <netinet/in.h>
27#include <netinet/tcp.h>
28#include <signal.h>
29
30#include "config.h"
31#ifdef TARGET_I386
32#include "cpu-i386.h"
33#endif
34#ifdef TARGET_ARM
35#include "cpu-arm.h"
36#endif
37#include "thunk.h"
38#include "exec.h"
39
4c3a88a2 40#define DEBUG_GDB
b4608c04
FB
41
42int gdbstub_fd = -1;
43
44/* return 0 if OK */
45static int gdbstub_open(int port)
46{
47 struct sockaddr_in sockaddr;
48 socklen_t len;
49 int fd, val, ret;
50
51 fd = socket(PF_INET, SOCK_STREAM, 0);
52 if (fd < 0) {
53 perror("socket");
54 return -1;
55 }
56
57 /* allow fast reuse */
58 val = 1;
59 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
60
61 sockaddr.sin_family = AF_INET;
62 sockaddr.sin_port = htons(port);
63 sockaddr.sin_addr.s_addr = 0;
64 ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
65 if (ret < 0) {
66 perror("bind");
67 return -1;
68 }
69 ret = listen(fd, 0);
70 if (ret < 0) {
71 perror("listen");
72 return -1;
73 }
74
75 /* now wait for one connection */
76 for(;;) {
77 len = sizeof(sockaddr);
78 gdbstub_fd = accept(fd, (struct sockaddr *)&sockaddr, &len);
79 if (gdbstub_fd < 0 && errno != EINTR) {
80 perror("accept");
81 return -1;
82 } else if (gdbstub_fd >= 0) {
83 break;
84 }
85 }
86
87 /* set short latency */
88 val = 1;
89 setsockopt(gdbstub_fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
90 return 0;
91}
92
93static int get_char(void)
94{
95 uint8_t ch;
96 int ret;
97
98 for(;;) {
99 ret = read(gdbstub_fd, &ch, 1);
100 if (ret < 0) {
101 if (errno != EINTR && errno != EAGAIN)
102 return -1;
103 } else if (ret == 0) {
104 return -1;
105 } else {
106 break;
107 }
108 }
109 return ch;
110}
111
112static void put_buffer(const uint8_t *buf, int len)
113{
114 int ret;
115
116 while (len > 0) {
117 ret = write(gdbstub_fd, buf, len);
118 if (ret < 0) {
119 if (errno != EINTR && errno != EAGAIN)
120 return;
121 } else {
122 buf += ret;
123 len -= ret;
124 }
125 }
126}
127
128static inline int fromhex(int v)
129{
130 if (v >= '0' && v <= '9')
131 return v - '0';
132 else if (v >= 'A' && v <= 'F')
133 return v - 'A' + 10;
134 else if (v >= 'a' && v <= 'f')
135 return v - 'a' + 10;
136 else
137 return 0;
138}
139
140static inline int tohex(int v)
141{
142 if (v < 10)
143 return v + '0';
144 else
145 return v - 10 + 'a';
146}
147
148static void memtohex(char *buf, const uint8_t *mem, int len)
149{
150 int i, c;
151 char *q;
152 q = buf;
153 for(i = 0; i < len; i++) {
154 c = mem[i];
155 *q++ = tohex(c >> 4);
156 *q++ = tohex(c & 0xf);
157 }
158 *q = '\0';
159}
160
161static void hextomem(uint8_t *mem, const char *buf, int len)
162{
163 int i;
164
165 for(i = 0; i < len; i++) {
166 mem[i] = (fromhex(buf[0]) << 4) | fromhex(buf[1]);
167 buf += 2;
168 }
169}
170
171/* return -1 if error or EOF */
172static int get_packet(char *buf, int buf_size)
173{
174 int ch, len, csum, csum1;
175 char reply[1];
176
177 for(;;) {
178 for(;;) {
179 ch = get_char();
180 if (ch < 0)
181 return -1;
182 if (ch == '$')
183 break;
184 }
185 len = 0;
186 csum = 0;
187 for(;;) {
188 ch = get_char();
189 if (ch < 0)
190 return -1;
191 if (ch == '#')
192 break;
193 if (len > buf_size - 1)
194 return -1;
195 buf[len++] = ch;
196 csum += ch;
197 }
198 buf[len] = '\0';
199 ch = get_char();
200 if (ch < 0)
201 return -1;
202 csum1 = fromhex(ch) << 4;
203 ch = get_char();
204 if (ch < 0)
205 return -1;
206 csum1 |= fromhex(ch);
207 if ((csum & 0xff) != csum1) {
208 reply[0] = '-';
209 put_buffer(reply, 1);
210 } else {
211 reply[0] = '+';
212 put_buffer(reply, 1);
213 break;
214 }
215 }
216#ifdef DEBUG_GDB
217 printf("command='%s'\n", buf);
218#endif
219 return len;
220}
221
222/* return -1 if error, 0 if OK */
223static int put_packet(char *buf)
224{
225 char buf1[3];
226 int len, csum, ch, i;
227
228#ifdef DEBUG_GDB
229 printf("reply='%s'\n", buf);
230#endif
231
232 for(;;) {
233 buf1[0] = '$';
234 put_buffer(buf1, 1);
235 len = strlen(buf);
236 put_buffer(buf, len);
237 csum = 0;
238 for(i = 0; i < len; i++) {
239 csum += buf[i];
240 }
241 buf1[0] = '#';
242 buf1[1] = tohex((csum >> 4) & 0xf);
243 buf1[2] = tohex((csum) & 0xf);
244
245 put_buffer(buf1, 3);
246
247 ch = get_char();
248 if (ch < 0)
249 return -1;
250 if (ch == '+')
251 break;
252 }
253 return 0;
254}
255
256static int memory_rw(uint8_t *buf, uint32_t addr, int len, int is_write)
257{
258 int l, flags;
259 uint32_t page;
260
261 while (len > 0) {
262 page = addr & TARGET_PAGE_MASK;
263 l = (page + TARGET_PAGE_SIZE) - addr;
264 if (l > len)
265 l = len;
266 flags = page_get_flags(page);
267 if (!(flags & PAGE_VALID))
268 return -1;
269 if (is_write) {
270 if (!(flags & PAGE_WRITE))
271 return -1;
272 memcpy((uint8_t *)addr, buf, l);
273 } else {
274 if (!(flags & PAGE_READ))
275 return -1;
276 memcpy(buf, (uint8_t *)addr, l);
277 }
278 len -= l;
279 buf += l;
280 addr += l;
281 }
282 return 0;
283}
284
285/* port = 0 means default port */
4c3a88a2 286int cpu_gdbstub(void *opaque, int (*main_loop)(void *opaque), int port)
b4608c04
FB
287{
288 CPUState *env;
289 const char *p;
4c3a88a2 290 int ret, ch, nb_regs, i, type;
b4608c04
FB
291 char buf[4096];
292 uint8_t mem_buf[2000];
293 uint32_t *registers;
294 uint32_t addr, len;
295
296 printf("Waiting gdb connection on port %d\n", port);
297 if (gdbstub_open(port) < 0)
298 return -1;
299 printf("Connected\n");
300 for(;;) {
301 ret = get_packet(buf, sizeof(buf));
302 if (ret < 0)
303 break;
304 p = buf;
305 ch = *p++;
306 switch(ch) {
307 case '?':
308 snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
309 put_packet(buf);
310 break;
311 case 'c':
4c3a88a2
FB
312 if (*p != '\0') {
313 addr = strtoul(p, (char **)&p, 16);
314 env = cpu_gdbstub_get_env(opaque);
315#if defined(TARGET_I386)
316 env->eip = addr;
317#endif
318 }
319 ret = main_loop(opaque);
320 if (ret == EXCP_DEBUG)
321 ret = SIGTRAP;
322 else
323 ret = 0;
324 snprintf(buf, sizeof(buf), "S%02x", ret);
b4608c04
FB
325 put_packet(buf);
326 break;
327 case 'g':
328 env = cpu_gdbstub_get_env(opaque);
329 registers = (void *)mem_buf;
330#if defined(TARGET_I386)
331 for(i = 0; i < 8; i++) {
332 registers[i] = tswapl(env->regs[i]);
333 }
334 registers[8] = env->eip;
335 registers[9] = env->eflags;
336 registers[10] = env->segs[R_CS].selector;
337 registers[11] = env->segs[R_SS].selector;
338 registers[12] = env->segs[R_DS].selector;
339 registers[13] = env->segs[R_ES].selector;
340 registers[14] = env->segs[R_FS].selector;
341 registers[15] = env->segs[R_GS].selector;
342 nb_regs = 16;
343#endif
344 memtohex(buf, (const uint8_t *)registers,
345 sizeof(registers[0]) * nb_regs);
346 put_packet(buf);
347 break;
348 case 'G':
349 env = cpu_gdbstub_get_env(opaque);
350 registers = (void *)mem_buf;
351#if defined(TARGET_I386)
352 hextomem((uint8_t *)registers, p, 16 * 4);
353 for(i = 0; i < 8; i++) {
354 env->regs[i] = tswapl(registers[i]);
355 }
356 env->eip = registers[8];
357 env->eflags = registers[9];
358#define LOAD_SEG(index, sreg)\
359 if (tswapl(registers[index]) != env->segs[sreg].selector)\
360 cpu_x86_load_seg(env, sreg, tswapl(registers[index]));
361 LOAD_SEG(10, R_CS);
362 LOAD_SEG(11, R_SS);
363 LOAD_SEG(12, R_DS);
364 LOAD_SEG(13, R_ES);
365 LOAD_SEG(14, R_FS);
366 LOAD_SEG(15, R_GS);
367#endif
368 put_packet("OK");
369 break;
370 case 'm':
371 addr = strtoul(p, (char **)&p, 16);
372 if (*p == ',')
373 p++;
374 len = strtoul(p, NULL, 16);
375 if (memory_rw(mem_buf, addr, len, 0) != 0)
376 memset(mem_buf, 0, len);
377 memtohex(buf, mem_buf, len);
378 put_packet(buf);
379 break;
380 case 'M':
381 addr = strtoul(p, (char **)&p, 16);
382 if (*p == ',')
383 p++;
384 len = strtoul(p, (char **)&p, 16);
385 if (*p == ',')
386 p++;
387 hextomem(mem_buf, p, len);
388 if (memory_rw(mem_buf, addr, len, 1) != 0)
389 put_packet("ENN");
390 else
391 put_packet("OK");
392 break;
4c3a88a2
FB
393 case 'Z':
394 type = strtoul(p, (char **)&p, 16);
395 if (*p == ',')
396 p++;
397 addr = strtoul(p, (char **)&p, 16);
398 if (*p == ',')
399 p++;
400 len = strtoul(p, (char **)&p, 16);
401 if (type == 0 || type == 1) {
402 env = cpu_gdbstub_get_env(opaque);
403 if (cpu_breakpoint_insert(env, addr) < 0)
404 goto breakpoint_error;
405 put_packet("OK");
406 } else {
407 breakpoint_error:
408 put_packet("ENN");
409 }
410 break;
411 case 'z':
412 type = strtoul(p, (char **)&p, 16);
413 if (*p == ',')
414 p++;
415 addr = strtoul(p, (char **)&p, 16);
416 if (*p == ',')
417 p++;
418 len = strtoul(p, (char **)&p, 16);
419 if (type == 0 || type == 1) {
420 env = cpu_gdbstub_get_env(opaque);
421 cpu_breakpoint_remove(env, addr);
422 put_packet("OK");
423 } else {
424 goto breakpoint_error;
425 }
426 break;
b4608c04
FB
427 default:
428 /* put empty packet */
429 buf[0] = '\0';
430 put_packet(buf);
431 break;
432 }
433 }
434 return 0;
435}