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