Commit 4046d9130ebf3fb4dbb3fa49dfc7e23df7e59d87
1 parent
f7499989
Use standard character device interface for gdbstub.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2363 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
5 changed files
with
118 additions
and
78 deletions
gdbstub.c
... | ... | @@ -53,25 +53,28 @@ enum RSState { |
53 | 53 | RS_CHKSUM1, |
54 | 54 | RS_CHKSUM2, |
55 | 55 | }; |
56 | -/* XXX: This is not thread safe. Do we care? */ | |
57 | -static int gdbserver_fd = -1; | |
58 | - | |
59 | 56 | typedef struct GDBState { |
60 | 57 | CPUState *env; /* current CPU */ |
61 | 58 | enum RSState state; /* parsing state */ |
62 | - int fd; | |
63 | 59 | char line_buf[4096]; |
64 | 60 | int line_buf_index; |
65 | 61 | int line_csum; |
62 | + char last_packet[4100]; | |
63 | + int last_packet_len; | |
66 | 64 | #ifdef CONFIG_USER_ONLY |
65 | + int fd; | |
67 | 66 | int running_state; |
67 | +#else | |
68 | + CharDriverState *chr; | |
68 | 69 | #endif |
69 | 70 | } GDBState; |
70 | 71 | |
71 | 72 | #ifdef CONFIG_USER_ONLY |
73 | +/* XXX: This is not thread safe. Do we care? */ | |
74 | +static int gdbserver_fd = -1; | |
75 | + | |
72 | 76 | /* XXX: remove this hack. */ |
73 | 77 | static GDBState gdbserver_state; |
74 | -#endif | |
75 | 78 | |
76 | 79 | static int get_char(GDBState *s) |
77 | 80 | { |
... | ... | @@ -91,9 +94,11 @@ static int get_char(GDBState *s) |
91 | 94 | } |
92 | 95 | return ch; |
93 | 96 | } |
97 | +#endif | |
94 | 98 | |
95 | 99 | static void put_buffer(GDBState *s, const uint8_t *buf, int len) |
96 | 100 | { |
101 | +#ifdef CONFIG_USER_ONLY | |
97 | 102 | int ret; |
98 | 103 | |
99 | 104 | while (len > 0) { |
... | ... | @@ -106,6 +111,9 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int len) |
106 | 111 | len -= ret; |
107 | 112 | } |
108 | 113 | } |
114 | +#else | |
115 | + qemu_chr_write(s->chr, buf, len); | |
116 | +#endif | |
109 | 117 | } |
110 | 118 | |
111 | 119 | static inline int fromhex(int v) |
... | ... | @@ -154,33 +162,39 @@ static void hextomem(uint8_t *mem, const char *buf, int len) |
154 | 162 | /* return -1 if error, 0 if OK */ |
155 | 163 | static int put_packet(GDBState *s, char *buf) |
156 | 164 | { |
157 | - char buf1[3]; | |
158 | - int len, csum, ch, i; | |
165 | + int len, csum, i; | |
166 | + char *p; | |
159 | 167 | |
160 | 168 | #ifdef DEBUG_GDB |
161 | 169 | printf("reply='%s'\n", buf); |
162 | 170 | #endif |
163 | 171 | |
164 | 172 | for(;;) { |
165 | - buf1[0] = '$'; | |
166 | - put_buffer(s, buf1, 1); | |
173 | + p = s->last_packet; | |
174 | + *(p++) = '$'; | |
167 | 175 | len = strlen(buf); |
168 | - put_buffer(s, buf, len); | |
176 | + memcpy(p, buf, len); | |
177 | + p += len; | |
169 | 178 | csum = 0; |
170 | 179 | for(i = 0; i < len; i++) { |
171 | 180 | csum += buf[i]; |
172 | 181 | } |
173 | - buf1[0] = '#'; | |
174 | - buf1[1] = tohex((csum >> 4) & 0xf); | |
175 | - buf1[2] = tohex((csum) & 0xf); | |
182 | + *(p++) = '#'; | |
183 | + *(p++) = tohex((csum >> 4) & 0xf); | |
184 | + *(p++) = tohex((csum) & 0xf); | |
176 | 185 | |
177 | - put_buffer(s, buf1, 3); | |
186 | + s->last_packet_len = p - s->last_packet; | |
187 | + put_buffer(s, s->last_packet, s->last_packet_len); | |
178 | 188 | |
179 | - ch = get_char(s); | |
180 | - if (ch < 0) | |
189 | +#ifdef CONFIG_USER_ONLY | |
190 | + i = get_char(s); | |
191 | + if (i < 0) | |
181 | 192 | return -1; |
182 | - if (ch == '+') | |
193 | + if (i == '+') | |
183 | 194 | break; |
195 | +#else | |
196 | + break; | |
197 | +#endif | |
184 | 198 | } |
185 | 199 | return 0; |
186 | 200 | } |
... | ... | @@ -864,6 +878,26 @@ static void gdb_read_byte(GDBState *s, int ch) |
864 | 878 | char reply[1]; |
865 | 879 | |
866 | 880 | #ifndef CONFIG_USER_ONLY |
881 | + if (s->last_packet_len) { | |
882 | + /* Waiting for a response to the last packet. If we see the start | |
883 | + of a new command then abandon the previous response. */ | |
884 | + if (ch == '-') { | |
885 | +#ifdef DEBUG_GDB | |
886 | + printf("Got NACK, retransmitting\n"); | |
887 | +#endif | |
888 | + put_buffer(s, s->last_packet, s->last_packet_len); | |
889 | + } | |
890 | +#ifdef DEBUG_GDB | |
891 | + else if (ch == '+') | |
892 | + printf("Got ACK\n"); | |
893 | + else | |
894 | + printf("Got '%c' when expecting ACK/NACK\n", ch); | |
895 | +#endif | |
896 | + if (ch == '+' || ch == '$') | |
897 | + s->last_packet_len = 0; | |
898 | + if (ch != '$') | |
899 | + return; | |
900 | + } | |
867 | 901 | if (vm_running) { |
868 | 902 | /* when the CPU is running, we cannot do anything except stop |
869 | 903 | it when receiving a char */ |
... | ... | @@ -972,30 +1006,6 @@ void gdb_exit(CPUState *env, int code) |
972 | 1006 | put_packet(s, buf); |
973 | 1007 | } |
974 | 1008 | |
975 | -#else | |
976 | -static void gdb_read(void *opaque) | |
977 | -{ | |
978 | - GDBState *s = opaque; | |
979 | - int i, size; | |
980 | - uint8_t buf[4096]; | |
981 | - | |
982 | - size = recv(s->fd, buf, sizeof(buf), 0); | |
983 | - if (size < 0) | |
984 | - return; | |
985 | - if (size == 0) { | |
986 | - /* end of connection */ | |
987 | - qemu_del_vm_stop_handler(gdb_vm_stopped, s); | |
988 | - qemu_set_fd_handler(s->fd, NULL, NULL, NULL); | |
989 | - qemu_free(s); | |
990 | - if (autostart) | |
991 | - vm_start(); | |
992 | - } else { | |
993 | - for(i = 0; i < size; i++) | |
994 | - gdb_read_byte(s, buf[i]); | |
995 | - } | |
996 | -} | |
997 | - | |
998 | -#endif | |
999 | 1009 | |
1000 | 1010 | static void gdb_accept(void *opaque) |
1001 | 1011 | { |
... | ... | @@ -1019,32 +1029,12 @@ static void gdb_accept(void *opaque) |
1019 | 1029 | val = 1; |
1020 | 1030 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); |
1021 | 1031 | |
1022 | -#ifdef CONFIG_USER_ONLY | |
1023 | 1032 | s = &gdbserver_state; |
1024 | 1033 | memset (s, 0, sizeof (GDBState)); |
1025 | -#else | |
1026 | - s = qemu_mallocz(sizeof(GDBState)); | |
1027 | - if (!s) { | |
1028 | - close(fd); | |
1029 | - return; | |
1030 | - } | |
1031 | -#endif | |
1032 | 1034 | s->env = first_cpu; /* XXX: allow to change CPU */ |
1033 | 1035 | s->fd = fd; |
1034 | 1036 | |
1035 | -#ifdef CONFIG_USER_ONLY | |
1036 | 1037 | fcntl(fd, F_SETFL, O_NONBLOCK); |
1037 | -#else | |
1038 | - socket_set_nonblock(fd); | |
1039 | - | |
1040 | - /* stop the VM */ | |
1041 | - vm_stop(EXCP_INTERRUPT); | |
1042 | - | |
1043 | - /* start handling I/O */ | |
1044 | - qemu_set_fd_handler(s->fd, gdb_read, NULL, s); | |
1045 | - /* when the VM is stopped, the following callback is called */ | |
1046 | - qemu_add_vm_stop_handler(gdb_vm_stopped, s); | |
1047 | -#endif | |
1048 | 1038 | } |
1049 | 1039 | |
1050 | 1040 | static int gdbserver_open(int port) |
... | ... | @@ -1075,9 +1065,6 @@ static int gdbserver_open(int port) |
1075 | 1065 | perror("listen"); |
1076 | 1066 | return -1; |
1077 | 1067 | } |
1078 | -#ifndef CONFIG_USER_ONLY | |
1079 | - socket_set_nonblock(fd); | |
1080 | -#endif | |
1081 | 1068 | return fd; |
1082 | 1069 | } |
1083 | 1070 | |
... | ... | @@ -1087,10 +1074,52 @@ int gdbserver_start(int port) |
1087 | 1074 | if (gdbserver_fd < 0) |
1088 | 1075 | return -1; |
1089 | 1076 | /* accept connections */ |
1090 | -#ifdef CONFIG_USER_ONLY | |
1091 | 1077 | gdb_accept (NULL); |
1078 | + return 0; | |
1079 | +} | |
1092 | 1080 | #else |
1093 | - qemu_set_fd_handler(gdbserver_fd, gdb_accept, NULL, NULL); | |
1094 | -#endif | |
1081 | +static int gdb_chr_can_recieve(void *opaque) | |
1082 | +{ | |
1083 | + return 1; | |
1084 | +} | |
1085 | + | |
1086 | +static void gdb_chr_recieve(void *opaque, const uint8_t *buf, int size) | |
1087 | +{ | |
1088 | + GDBState *s = opaque; | |
1089 | + int i; | |
1090 | + | |
1091 | + for (i = 0; i < size; i++) { | |
1092 | + gdb_read_byte(s, buf[i]); | |
1093 | + } | |
1094 | +} | |
1095 | + | |
1096 | +static void gdb_chr_event(void *opaque, int event) | |
1097 | +{ | |
1098 | + switch (event) { | |
1099 | + case CHR_EVENT_RESET: | |
1100 | + vm_stop(EXCP_INTERRUPT); | |
1101 | + break; | |
1102 | + default: | |
1103 | + break; | |
1104 | + } | |
1105 | +} | |
1106 | + | |
1107 | +int gdbserver_start(CharDriverState *chr) | |
1108 | +{ | |
1109 | + GDBState *s; | |
1110 | + | |
1111 | + if (!chr) | |
1112 | + return -1; | |
1113 | + | |
1114 | + s = qemu_mallocz(sizeof(GDBState)); | |
1115 | + if (!s) { | |
1116 | + return -1; | |
1117 | + } | |
1118 | + s->env = first_cpu; /* XXX: allow to change CPU */ | |
1119 | + s->chr = chr; | |
1120 | + qemu_chr_add_handlers(chr, gdb_chr_can_recieve, gdb_chr_recieve, | |
1121 | + gdb_chr_event, s); | |
1122 | + qemu_add_vm_stop_handler(gdb_vm_stopped, s); | |
1095 | 1123 | return 0; |
1096 | 1124 | } |
1125 | +#endif | ... | ... |
gdbstub.h
qemu-doc.texi
... | ... | @@ -631,7 +631,8 @@ non graphical mode. |
631 | 631 | @item -s |
632 | 632 | Wait gdb connection to port 1234 (@pxref{gdb_usage}). |
633 | 633 | @item -p port |
634 | -Change gdb connection port. | |
634 | +Change gdb connection port. @var{port} can be either a decimal number | |
635 | +to specify a TCP port, or a host device (same devices as the serial port). | |
635 | 636 | @item -S |
636 | 637 | Do not start CPU at startup (you must type 'c' in the monitor). |
637 | 638 | @item -d | ... | ... |
vl.c
... | ... | @@ -6496,7 +6496,8 @@ static BOOL WINAPI qemu_ctrl_handler(DWORD type) |
6496 | 6496 | int main(int argc, char **argv) |
6497 | 6497 | { |
6498 | 6498 | #ifdef CONFIG_GDBSTUB |
6499 | - int use_gdbstub, gdbstub_port; | |
6499 | + int use_gdbstub; | |
6500 | + char gdbstub_port_name[128]; | |
6500 | 6501 | #endif |
6501 | 6502 | int i, cdrom_index; |
6502 | 6503 | int snapshot, linux_boot; |
... | ... | @@ -6564,7 +6565,7 @@ int main(int argc, char **argv) |
6564 | 6565 | bios_size = BIOS_SIZE; |
6565 | 6566 | #ifdef CONFIG_GDBSTUB |
6566 | 6567 | use_gdbstub = 0; |
6567 | - gdbstub_port = DEFAULT_GDBSTUB_PORT; | |
6568 | + sprintf(gdbstub_port_name, "%d", DEFAULT_GDBSTUB_PORT); | |
6568 | 6569 | #endif |
6569 | 6570 | snapshot = 0; |
6570 | 6571 | nographic = 0; |
... | ... | @@ -6808,7 +6809,7 @@ int main(int argc, char **argv) |
6808 | 6809 | use_gdbstub = 1; |
6809 | 6810 | break; |
6810 | 6811 | case QEMU_OPTION_p: |
6811 | - gdbstub_port = atoi(optarg); | |
6812 | + pstrcpy(gdbstub_port_name, sizeof(gdbstub_port_name), optarg); | |
6812 | 6813 | break; |
6813 | 6814 | #endif |
6814 | 6815 | case QEMU_OPTION_L: |
... | ... | @@ -7216,13 +7217,19 @@ int main(int argc, char **argv) |
7216 | 7217 | |
7217 | 7218 | #ifdef CONFIG_GDBSTUB |
7218 | 7219 | if (use_gdbstub) { |
7219 | - if (gdbserver_start(gdbstub_port) < 0) { | |
7220 | - fprintf(stderr, "Could not open gdbserver socket on port %d\n", | |
7221 | - gdbstub_port); | |
7220 | + CharDriverState *chr; | |
7221 | + int port; | |
7222 | + | |
7223 | + port = atoi(gdbstub_port_name); | |
7224 | + if (port != 0) | |
7225 | + sprintf(gdbstub_port_name, "tcp::%d,nowait,nodelay,server", port); | |
7226 | + chr = qemu_chr_open(gdbstub_port_name); | |
7227 | + if (!chr) { | |
7228 | + fprintf(stderr, "qemu: could not open gdbstub device '%s'\n", | |
7229 | + gdbstub_port_name); | |
7222 | 7230 | exit(1); |
7223 | - } else { | |
7224 | - printf("Waiting gdb connection on port %d\n", gdbstub_port); | |
7225 | 7231 | } |
7232 | + gdbserver_start(chr); | |
7226 | 7233 | } else |
7227 | 7234 | #endif |
7228 | 7235 | if (loadvm) | ... | ... |
vl.h
... | ... | @@ -84,7 +84,6 @@ static inline char *realpath(const char *path, char *resolved_path) |
84 | 84 | |
85 | 85 | #include "audio/audio.h" |
86 | 86 | #include "cpu.h" |
87 | -#include "gdbstub.h" | |
88 | 87 | |
89 | 88 | #endif /* !defined(QEMU_TOOL) */ |
90 | 89 | |
... | ... | @@ -1364,6 +1363,8 @@ pflash_t *pflash_register (target_ulong base, ram_addr_t off, |
1364 | 1363 | uint16_t id0, uint16_t id1, |
1365 | 1364 | uint16_t id2, uint16_t id3); |
1366 | 1365 | |
1366 | +#include "gdbstub.h" | |
1367 | + | |
1367 | 1368 | #endif /* defined(QEMU_TOOL) */ |
1368 | 1369 | |
1369 | 1370 | /* monitor.c */ | ... | ... |