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