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, &regs, 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, &regs.u.gregs_32);
361     else
362         rbuf = copy_mini_context64(rbuf, &regs.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