1 /*
2 * Copyright (C) 2009, Mukesh Rathor, Oracle Corp. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public
14 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 /* This module is the main module for gdbsx implementation. gdbsx is a remote
18 * gdbserver stub for xen. It facilitates debugging of xen guests. It also
19 * prints vcpu contexts locally without remote gdb. */
20
21 #include <stdio.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <inttypes.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <assert.h>
29 #include <signal.h>
30
31 #include "gx.h"
32
33
34 enum target_signal {
35 TARGET_SIGNAL_INT = 2,
36 TARGET_SIGNAL_TRAP = 5
37 };
38
39
40 /* At present, we don't support offlining VCPUs, or dynamic adding/removal
41 * of them. As such, max_vcpu means active [0 - max_vcpuid] vcpus */
42 vcpuid_t max_vcpuid; /* so max_vcpuid+1 vcpus overall */
43 int guest_bitness; /* 32 or 64 */
44
45 const char host_name[] = "";
46
47 int gx_remote_dbg; /* enable debug trace output for debugging */
48 uint64_t pgd3val; /* value of init_mm.pgd[3] set by monitor gdb cmd */
49
50 static vcpuid_t current_vcpu;
51
52 /*
53 * write regs received from remote gdb to guest
54 */
55 static void
gx_write_guest_regs(char * rbuf)56 gx_write_guest_regs(char *rbuf)
57 {
58 union xg_gdb_regs gregs;
59 int rc;
60 char *savrbuf = rbuf;
61 int regsz = (guest_bitness == 32) ? sizeof(gregs.gregs_32) :
62 sizeof(gregs.gregs_64);
63 rbuf++;
64 if (strlen(rbuf) != 2*regsz) {
65 gxprt("ERROR: wrong sized register pkt received...\n"
66 "Expected:%d got:%d\n", 2*regsz, strlen(rbuf));
67 }
68 gx_convert_ascii_to_int(rbuf, (char *)&gregs, regsz);
69
70 rc = xg_regs_write(XG_GPRS, current_vcpu, &gregs, guest_bitness);
71 if (rc) {
72 gxprt("ERROR: failed to write regs. errno:%d\n", errno);
73 savrbuf[0] ='\0';
74 gx_reply_error(savrbuf);
75 } else {
76 gx_reply_ok(savrbuf);
77 }
78 }
79
80 /*
81 * read guest regs and send to remote gdb
82 */
83 static void
gx_read_guest_regs(char * rbuf)84 gx_read_guest_regs(char *rbuf)
85 {
86 union xg_gdb_regs gregs;
87 int rc;
88
89 rc = xg_regs_read(XG_GPRS, current_vcpu, &gregs, guest_bitness);
90 if (rc) {
91 gxprt("ERROR: failed to read regs. errno:%d\n", errno);
92 rbuf[0] ='\0';
93 } else {
94 int sz = (guest_bitness == 32) ? sizeof(gregs.gregs_32) :
95 sizeof(gregs.gregs_64);
96 gx_convert_int_to_ascii((char *)&gregs, rbuf, sz);
97 }
98 }
99
100 /* remote_buf: 'qRcmd,pgd3 c0ae9018\0' (c0ae9018 may also be 0xc0ae9018) */
101 static void
_do_qRcmd_req(char * remote_buf)102 _do_qRcmd_req(char *remote_buf)
103 {
104 char buf[64], buf1[64];
105 char *p = remote_buf + 6;
106 int len = strlen(p)/2; /* because "70" is one char "p" */
107
108 gx_convert_ascii_to_int(p, buf, len);
109 XGTRC("remote_buf:%s buf:%s\n", remote_buf, buf);
110
111 if (strncmp(buf, "pgd3 ", 5) == 0) {
112 char *endp;
113
114 pgd3val = strtoull(buf+5, &endp, 16);
115 XGTRC("buf+5:%s pgd3val:0x%llx\n", buf+5, pgd3val);
116
117 if (*endp == '\0' && pgd3val > 0) {
118 sprintf(buf1, "pgd3val set to: %016"PRIx64"\n", pgd3val);
119 } else {
120 sprintf(buf1, "Invalid pgd3val %016"PRIx64"\n", pgd3val);
121 pgd3val = 0;
122 }
123 } else {
124 sprintf(buf1, "Bad monitor command\n");
125 }
126 gx_convert_int_to_ascii(buf1, remote_buf, strlen(buf1));
127 return;
128 }
129
130 /* qSupported qC qfThreadInfo qsThreadInfo qThreadExtraInfo,1 etc.. */
131 static void
process_q_request(char * remote_buf)132 process_q_request(char *remote_buf)
133 {
134 /* send a list of tids: "m0,1,2,3l" */
135 if (strcmp("qfThreadInfo", remote_buf) == 0) {
136 vcpuid_t vid = 0;
137 char *p = remote_buf;
138
139 sprintf(p, "m%x", vid); /* puts null char at the end */
140 p = p + strlen(p);
141 for (vid=1; vid <= max_vcpuid; vid++) {
142 sprintf(p, ",%x", vid);
143 p = p + strlen(p);
144 }
145 sprintf(p, "l"); /* puts null char at the end */
146 return;
147 }
148
149 /* qSymbol works for init_mm, and not init_mm.pgd, hence we can't use
150 * it at this time. instead use "monitor" in gdb */
151 if (strncmp("qRcmd,", remote_buf, 6) == 0) {
152 _do_qRcmd_req(remote_buf);
153 return;
154 }
155
156 /* TBD : qThreadExtraInfo : send extra banner info */
157
158 remote_buf[0] = '\0'; /* nothing else supported for now */
159
160 return;
161 }
162
163 /*
164 * Set current thread/vcpu to : -1 all threads, 0 any thread, or given tid/vcpu
165 * Even tho, 0 is a valid vcpu for us, it's OK as vcpu 0 is any vcpu
166 * Eg. Hc-1\0 Hc0\0 etc...
167 */
168 static void
process_H_request(char * remote_buf)169 process_H_request(char *remote_buf)
170 {
171 char ch1 = remote_buf[1];
172
173 if (ch1 == 'c' || ch1 == 'g' || ch1 == 's') {
174 vcpuid_t vcpu;
175
176 /* we keep vcpu_id (which gdb thinks is tid) and
177 * gdb_id the same for simplicity */
178
179 vcpu = strtoul(&remote_buf[2], NULL, 16);
180 if (vcpu == -1) {
181 vcpu = 0;
182 }
183 /* it doesn't matter to us: g, c, or s */
184 current_vcpu = vcpu;
185 gx_reply_ok(remote_buf);
186 } else {
187 /* Silently ignore so gdb can extend the protocol
188 * without compatibility headaches */
189 remote_buf[0] = '\0';
190 }
191 }
192
193 /* read guest memory to send to remote gdb user */
194 static void
process_m_request(char * remote_buf)195 process_m_request(char *remote_buf)
196 {
197 uint64_t addr;
198 int len, remain;
199 char *xbuf;
200
201 gx_decode_m_packet(&remote_buf[1], &addr, &len);
202
203 if ((xbuf=malloc(len+1)) == NULL) {
204 gx_reply_error(remote_buf);
205 return;
206 }
207 if ((remain=xg_read_mem(addr, xbuf, len, pgd3val)) != 0) {
208 XGTRC("Failed read mem. addr:0x%llx len:%d remn:%d errno:%d\n",
209 addr, len, remain, errno);
210 gx_reply_error(remote_buf);
211 free(xbuf);
212 return;
213 }
214 gx_convert_int_to_ascii(xbuf, remote_buf, len);
215 free(xbuf);
216 return;
217 }
218
219 /* write guest memory */
220 static void
process_M_request(char * remote_buf)221 process_M_request(char *remote_buf)
222 {
223 uint64_t addr;
224 int len, remain;
225 char *xbuf, *data_strtp; /* where guest data actually starts */
226
227 data_strtp = gx_decode_M_packet(&remote_buf[1], &addr, &len);
228
229 if ((xbuf=malloc(len+1)) == NULL) {
230 gx_reply_error(remote_buf);
231 return;
232 }
233 gx_convert_ascii_to_int(data_strtp, xbuf, len);
234
235 if ((remain=xg_write_mem(addr, xbuf, len, pgd3val)) != 0) {
236 gxprt("Failed write mem. addr:0x%llx len:%d rem:%d errno:%d\n",
237 addr, len, remain, errno);
238 gx_reply_error(remote_buf);
239 } else {
240 gx_reply_ok(remote_buf);
241 }
242 free(xbuf);
243 return;
244 }
245
246 /* Eg.: "vCont;c" "vCont;s:5" */
247 static void
process_v_cont_request(char * bufp)248 process_v_cont_request(char *bufp)
249 {
250 char *savbufp = bufp;
251
252 bufp = bufp + 5; /* address of semicolon */
253
254 if (*bufp == '\0' || *bufp != ';')
255 goto errout;
256 bufp++;
257 if (*bufp == 'S' || *bufp == 'C') /* we don't support signalling */
258 goto errout;
259 #if 0
260 if (*bufp == 'c') {
261 if (*(bufp+1) != '\0')
262 goto errout; /* don't tolerate bad pkt */
263 xg_resume(guest_bitness); /* continue domain */
264
265 } else if (*bufp == 's') {
266
267 /* we don't support : step vcpuid. user must switch to the
268 * thread/vcpu and then do step */
269 bufp++;
270 if (*bufp != '\0')
271 goto errout;
272 xg_step(current_vcpu, guest_bitness);
273 }
274 #endif
275 return;
276
277 errout:
278 savbufp[0] = '\0';
279 gxprt("WARN: Bad v pkt: %s\n", savbufp);
280 return;
281 }
282
283 static void
process_v_request(char * remote_buf)284 process_v_request(char *remote_buf)
285 {
286 if (strncmp(remote_buf, "vCont;", 6) == 0) {
287 process_v_cont_request(remote_buf); /* valid request */
288 return;
289 }
290 if (strncmp(remote_buf, "vCont?", 6) == 0) {
291 /* tell remote gdb what we support : c and s */
292 /* strcpy(remote_buf, "vCont;c;s"); */
293 remote_buf[0] = '\0';
294 return;
295 }
296 /* failed to understand the v packet */
297 remote_buf[0] = '\0';
298 return;
299 }
300
301 /* TBD: add watchpoint in future */
302 static int
watchpoint_stop(void)303 watchpoint_stop(void)
304 {
305 return 0;
306 }
307
308 #if 0
309 static char *
310 copy_mini_context32(char *rbuf, union xg_gdb_regs32 *regsp)
311 {
312 *rbuf++ = gx_tohex((EBP_IDX >> 4) & 0xf);
313 *rbuf++ = gx_tohex(EBP_IDX & 0xf);
314 *rbuf++ = ':';
315 rbuf = gx_convert_int_to_ascii(regsp->ebp, rbuf, 4);
316
317 *rbuf++ = gx_tohex((ESP_IDX >> 4) & 0xf);
318 *rbuf++ = gx_tohex(ESP_IDX & 0xf);
319 *rbuf++ = ':';
320 rbuf = gx_convert_int_to_ascii(regsp->esp, rbuf, 4);
321
322 *rbuf++ = gx_tohex((EIP_IDX >> 4) & 0xf);
323 *rbuf++ = gx_tohex(EIP_IDX & 0xf);
324 *rbuf++ = ':';
325 rbuf = gx_convert_int_to_ascii(regsp->eip, rbuf, 4);
326
327 return rbuf;
328 }
329
330 static char *
331 copy_mini_context64(char *rbuf, union xg_gdb_regs64 *regsp)
332 {
333 *rbuf++ = gx_tohex((RBP_IDX >> 4) & 0xf);
334 *rbuf++ = gx_tohex(RBP_IDX & 0xf);
335 *rbuf++ = ':';
336 rbuf = gx_convert_int_to_ascii(regsp->ebp, rbuf, 4);
337
338 *rbuf++ = gx_tohex((RSP_IDX >> 4) & 0xf);
339 *rbuf++ = gx_tohex(RSP_IDX & 0xf);
340 *rbuf++ = ':';
341 rbuf = gx_convert_int_to_ascii(regsp->esp, rbuf, 4);
342
343 *rbuf++ = gx_tohex((RIP_IDX >> 4) & 0xf);
344 *rbuf++ = gx_tohex(RIP_IDX & 0xf);
345 *rbuf++ = ':';
346 rbuf = gx_convert_int_to_ascii(regsp->eip, rbuf, 4);
347
348 return rbuf;
349 }
350
351 static char *
352 copy_mini_context(char *rbuf)
353 {
354 union xg_gdb_regs regs;
355
356 if (xg_regs_read(XG_GPRS, 0, ®s, guest_bitness)) {
357 gxprt("WARN: Unable to get read regs. errno:%d\n", errno);
358 return;
359 }
360 if (guest_bitness == 32)
361 rbuf = copy_mini_context32(rbuf, ®s.u.gregs_32);
362 else
363 rbuf = copy_mini_context64(rbuf, ®s.u.gregs_64);
364 return rbuf;
365 }
366
367 #endif
368
369 /*
370 * prepare reply for remote gdb as to why we stopped
371 */
372 static void
prepare_stop_reply(enum target_signal sig,char * buf,vcpuid_t vcpu)373 prepare_stop_reply(enum target_signal sig, char *buf, vcpuid_t vcpu)
374 {
375 int nib;
376
377 *buf++ = 'T'; /* we stopped because of a trap (SIGTRAP) */
378
379 nib = ((sig & 0xf0) >> 4);
380 *buf++ = gx_tohex(nib);
381 nib = sig & 0x0f;
382 *buf++ = gx_tohex(nib);
383
384 /* TBD: check if we stopped because of watchpoint */
385 if (watchpoint_stop()) {
386 memcpy(buf, "watch:", 6);
387 buf += 6;
388 /* TBD: **/
389 }
390 sprintf(buf, "thread:%x;", vcpu);
391 buf += strlen(buf);
392 *buf++ = '\0';
393 }
394 /*
395 * Indicate the reason the guest halted
396 */
397 static void
process_reas_request(char * remote_buf,vcpuid_t vcpu)398 process_reas_request(char *remote_buf, vcpuid_t vcpu)
399 {
400 prepare_stop_reply(TARGET_SIGNAL_TRAP, remote_buf, vcpu);
401 }
402
403 /* continue request */
404 static void
process_c_request(char * remote_buf)405 process_c_request(char *remote_buf)
406 {
407 enum target_signal sig;
408
409 if ((current_vcpu=xg_resume_n_wait(guest_bitness)) == -1) {
410 current_vcpu = 0; /* default vcpu */
411 sig = TARGET_SIGNAL_INT;
412 } else
413 sig = TARGET_SIGNAL_TRAP;
414
415 prepare_stop_reply(sig, remote_buf, current_vcpu);
416 }
417
418 #if 0
419 /* insert a bp: Z#,addr,len : where # is 0 for software bp, 1 for hardware bp,
420 * 2 is a write watchpoint, 3 is read watchpoint, 4 access watchpt
421 * We ignore len, it should always be 1.
422 * Eg: Z0,c0267d3a,1
423 */
424 static void
425 process_Z_request(char *rbuf)
426 {
427 char ch1 = rbuf[1];
428 uint64_t gva;
429
430 if (ch1 != '0') {
431 gx_reply_error(rbuf);
432 return;
433 }
434 gx_decode_zZ_packet(&rbuf[3], &gva);
435 if (xg_set_bp(gva, ch1))
436 gx_reply_error(rbuf);
437 else
438 gx_reply_ok(rbuf);
439 }
440
441 /* remove a bp */
442 static void
443 process_z_request(char *rbuf)
444 {
445 char ch1 = rbuf[1];
446 uint64_t gva;
447
448 if (ch1 != '0') {
449 gx_reply_error(rbuf);
450 return;
451 }
452 gx_decode_zZ_packet(&rbuf[3], &gva);
453 if (xg_rm_bp(gva, ch1))
454 gx_reply_error(rbuf);
455 else
456 gx_reply_ok(rbuf);
457 }
458 #endif
459
460 static int
process_remote_request(char * remote_buf)461 process_remote_request(char *remote_buf) /* buffer received from remote gdb */
462 {
463 char ch;
464 int rc=0, i=0;
465
466 XGTRC("E:%s curvcpu:%d\n", remote_buf, current_vcpu);
467
468 ch = remote_buf[i++];
469 switch(ch)
470 {
471 case 'q':
472 process_q_request(remote_buf);
473 break;
474
475 case 'd': /* print debug trace output */
476 gx_remote_dbg = !gx_remote_dbg;
477 printf("WARN: received d pkt:%s\n", remote_buf);
478 remote_buf[0] = '\0';
479 break;
480
481 case 'D':
482 gx_reply_ok(remote_buf);
483 rc = 1;
484 break;
485
486 case '?':
487 process_reas_request(remote_buf, 0);
488 break;
489
490 case 'H':
491 process_H_request(remote_buf);
492 break;
493
494 /* send general registers to remote gdb */
495 case 'g':
496 assert(current_vcpu != -1);
497 gx_read_guest_regs(remote_buf);
498 break;
499
500 /* receive general regs from remote gdb */
501 case 'G':
502 assert(current_vcpu != -1);
503 gx_write_guest_regs(remote_buf);
504 break;
505
506 /* read guest memory and send to remote gdb */
507 case 'm':
508 process_m_request(remote_buf);
509 break;
510
511 case 'M':
512 process_M_request(remote_buf);
513 break;
514
515 case 'C':
516 printf("WARN: C pkt: %s\n", remote_buf);
517 remote_buf[0] = '\0';
518 break;
519
520 case 'S':
521 printf("WARN: S pkt:%s\n", remote_buf);
522 remote_buf[0] = '\0';
523 break;
524
525 case 'c':
526 process_c_request(remote_buf); /* continue request */
527 break;
528
529 case 's': /* single step */
530 if (xg_step(current_vcpu, guest_bitness) != 0) {
531 remote_buf[0] = '\0';
532 } else {
533 prepare_stop_reply(TARGET_SIGNAL_TRAP, remote_buf,
534 current_vcpu);
535 }
536 break;
537
538 #if 0
539 case 'Z':
540 process_Z_request(remote_buf); /* insert a bp */
541 break;
542
543 case 'z':
544 process_z_request(remote_buf); /* remove a bp */
545 break;
546 #endif
547 case 'k': /* kill inferior */
548 printf("WARN: k pkt:%s\n", remote_buf);
549 remote_buf[0] = '\0';
550 break;
551
552 case 'T': /* find out if thread is alive */
553 gx_reply_ok(remote_buf); /* no vcpu offling supported yet */
554 break;
555
556 case 'R': /* TBD: restart gdbserver program */
557 /* Restarting the inferior is only supported in the
558 * extended protocol. */
559 remote_buf[0] = '\0';
560 break;
561
562 case 'v':
563 process_v_request(remote_buf);
564 break;
565
566 default:
567 /* It is a request we don't understand. Respond with an
568 * empty packet so that gdb knows that we don't support this
569 * request. */
570 remote_buf[0] = '\0';
571 break;
572
573 } /* end of switch(ch) */
574
575 XGTRC("X:%s curvcpu:%d\n", remote_buf, current_vcpu);
576 return rc;
577 }
578
579 static void
gdbsx_usage_exit(void)580 gdbsx_usage_exit(void)
581 {
582 printf ("Usage 1: gdbsx -a domid <32|64> PORT [-d]\n"
583 " PORT to listen for a TCP connection.\n"
584 " Eg. gdbsx -a 3 32 9999\n\n");
585 printf("Usage 2: gdbsx -c domid <32|64> [vcpu#] [-d]\n");
586 printf(" to dump vcpu context(s) for given domid\n\n");
587 exit(1);
588 }
589
590 static void
check_usage_n_stuff(int argc,char ** argv,domid_t * domid_p,vcpuid_t * vp)591 check_usage_n_stuff(int argc, char **argv, domid_t *domid_p, vcpuid_t *vp)
592 {
593 char *arg_end;
594
595 if (strcmp(argv[argc-1], "-d")==0) {
596 xgtrc_on = 1; /* debug trace on */
597 argc--;
598 }
599 if (argc < 4 || (strcmp(argv[1], "-h") == 0) ||
600 (strcmp(argv[1], "-a")==0 && argc < 5)) {
601 gdbsx_usage_exit();
602 }
603 if (argc > 5 ||
604 (*domid_p=strtoul(argv[2], &arg_end, 10)) == 0 ||
605 *arg_end != '\0' ||
606 *domid_p == 0 ||
607 (guest_bitness=strtoul(argv[3], &arg_end, 10)) == 0 ||
608 *arg_end != '\0' ||
609 (guest_bitness != 32 && guest_bitness != 64)) {
610
611 gdbsx_usage_exit();
612 }
613 *vp = -1; /* assume all VCPUs */
614 if (strcmp(argv[1], "-c")==0 && argc >= 5) {
615 *vp = strtoul(argv[4], &arg_end, 10);
616 if (*arg_end != '\0') {
617 gdbsx_usage_exit();
618 }
619 }
620 }
621
622 static void
initialize(char ** rbufpp)623 initialize(char **rbufpp)
624 {
625 #define BUFSIZE 4096
626
627 /* allocate buffer used to communicate back and forth with remote gdb */
628 /* size should be big enough to hold all registers + extra */
629 if ((*rbufpp=malloc(BUFSIZE)) == NULL) {
630 gxprt("ERROR: can't malloc %d bytes. errno:%d\n",
631 BUFSIZE, errno);
632 exit(3);
633 }
634 signal(SIGIO, SIG_IGN); /* default action is TERM */
635 }
636
637
638 int
main(int argc,char * argv[])639 main(int argc, char *argv[])
640 {
641 char *remote_buf;
642 domid_t domid = 0;
643 vcpuid_t vcpuid;
644 int exit_rc = 0;
645
646 check_usage_n_stuff(argc, argv, &domid, &vcpuid);
647
648 if (xg_init() == -1) {
649 gxprt("ERROR: failed to initialize errno:%d\n", errno);
650 exit(1);
651 }
652 if ((max_vcpuid=xg_attach(domid, guest_bitness)) == -1) {
653 gxprt("ERROR: failed to attach to domain:%d errno:%d\n",
654 domid, errno);
655 exit(1);
656 }
657 if (strcmp(argv[1], "-c")==0) {
658 if (vcpuid != -1 && vcpuid > max_vcpuid) { /* just got set */
659 printf("gdbsx: Invalid VCPU id:%d\n", vcpuid);
660 xg_detach_deinit();
661 gdbsx_usage_exit();
662 }
663 exit_rc = gx_local_cmd(domid, vcpuid);
664 xg_detach_deinit();
665 return exit_rc; /* EXIT */
666 }
667
668 initialize(&remote_buf);
669
670 /* we have the guest paused at this point, ready for debug. wait for
671 * connection from remote gdb */
672 if (gx_remote_open(argv[4]) == -1) {
673 xg_detach_deinit();
674 return 1;
675 }
676
677 /* we've a gdb connection at this point, process requests */
678 while(gx_getpkt(remote_buf) > 0) {
679 if ((exit_rc=process_remote_request(remote_buf)))
680 break;
681 if (gx_putpkt(remote_buf) == -1) {
682 exit_rc = 1;
683 break;
684 }
685 }
686 /* unpause and let the guest continue */
687 gxprt("Detaching from guest\n");
688 xg_detach_deinit();
689
690 if (exit_rc == 0) {
691 gxprt("Exiting.. Remote side has terminated connection\n");
692 }
693 gx_remote_close();
694 return exit_rc;
695 }
696