Commit a2d1ebaf890da03de850812cc8dbec2d56efb4e8
1 parent
4046d913
GDB hosted syscalls.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2364 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
262 additions
and
32 deletions
arm-semi.c
... | ... | @@ -61,7 +61,30 @@ |
61 | 61 | #define O_BINARY 0 |
62 | 62 | #endif |
63 | 63 | |
64 | -int open_modeflags[12] = { | |
64 | +#define GDB_O_RDONLY 0x000 | |
65 | +#define GDB_O_WRONLY 0x001 | |
66 | +#define GDB_O_RDWR 0x002 | |
67 | +#define GDB_O_APPEND 0x008 | |
68 | +#define GDB_O_CREAT 0x200 | |
69 | +#define GDB_O_TRUNC 0x400 | |
70 | +#define GDB_O_BINARY 0 | |
71 | + | |
72 | +static int gdb_open_modeflags[12] = { | |
73 | + GDB_O_RDONLY, | |
74 | + GDB_O_RDONLY | GDB_O_BINARY, | |
75 | + GDB_O_RDWR, | |
76 | + GDB_O_RDWR | GDB_O_BINARY, | |
77 | + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC, | |
78 | + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY, | |
79 | + GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC, | |
80 | + GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY, | |
81 | + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND, | |
82 | + GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY, | |
83 | + GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, | |
84 | + GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY | |
85 | +}; | |
86 | + | |
87 | +static int open_modeflags[12] = { | |
65 | 88 | O_RDONLY, |
66 | 89 | O_RDONLY | O_BINARY, |
67 | 90 | O_RDWR, |
... | ... | @@ -142,6 +165,35 @@ static void softmmu_unlock_user(CPUState *env, void *p, target_ulong addr, |
142 | 165 | #define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len) |
143 | 166 | #endif |
144 | 167 | |
168 | +static target_ulong arm_semi_syscall_len; | |
169 | + | |
170 | +static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err) | |
171 | +{ | |
172 | +#ifdef CONFIG_USER_ONLY | |
173 | + TaskState *ts = env->opaque; | |
174 | +#endif | |
175 | + if (ret == (target_ulong)-1) { | |
176 | +#ifdef CONFIG_USER_ONLY | |
177 | + ts->swi_errno = err; | |
178 | +#endif | |
179 | + env->regs[0] = ret; | |
180 | + } else { | |
181 | + /* Fixup syscalls that use nonstardard return conventions. */ | |
182 | + switch (env->regs[0]) { | |
183 | + case SYS_WRITE: | |
184 | + case SYS_READ: | |
185 | + env->regs[0] = arm_semi_syscall_len - ret; | |
186 | + break; | |
187 | + case SYS_SEEK: | |
188 | + env->regs[0] = 0; | |
189 | + break; | |
190 | + default: | |
191 | + env->regs[0] = ret; | |
192 | + break; | |
193 | + } | |
194 | + } | |
195 | +} | |
196 | + | |
145 | 197 | #define ARG(n) tget32(args + (n) * 4) |
146 | 198 | #define SET_ARG(n, val) tput32(args + (n) * 4,val) |
147 | 199 | uint32_t do_arm_semihosting(CPUState *env) |
... | ... | @@ -170,52 +222,99 @@ uint32_t do_arm_semihosting(CPUState *env) |
170 | 222 | else |
171 | 223 | return STDOUT_FILENO; |
172 | 224 | } |
173 | - ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644)); | |
225 | + if (use_gdb_syscalls()) { | |
226 | + gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0), (int)ARG(2), | |
227 | + gdb_open_modeflags[ARG(1)]); | |
228 | + return env->regs[0]; | |
229 | + } else { | |
230 | + ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644)); | |
231 | + } | |
174 | 232 | unlock_user(s, ARG(0), 0); |
175 | 233 | return ret; |
176 | 234 | case SYS_CLOSE: |
177 | - return set_swi_errno(ts, close(ARG(0))); | |
235 | + if (use_gdb_syscalls()) { | |
236 | + gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0)); | |
237 | + return env->regs[0]; | |
238 | + } else { | |
239 | + return set_swi_errno(ts, close(ARG(0))); | |
240 | + } | |
178 | 241 | case SYS_WRITEC: |
179 | 242 | { |
180 | 243 | char c = tget8(args); |
181 | 244 | /* Write to debug console. stderr is near enough. */ |
182 | - return write(STDERR_FILENO, &c, 1); | |
245 | + if (use_gdb_syscalls()) { | |
246 | + gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args); | |
247 | + return env->regs[0]; | |
248 | + } else { | |
249 | + return write(STDERR_FILENO, &c, 1); | |
250 | + } | |
183 | 251 | } |
184 | 252 | case SYS_WRITE0: |
185 | 253 | s = lock_user_string(args); |
186 | - ret = write(STDERR_FILENO, s, strlen(s)); | |
254 | + len = strlen(s); | |
255 | + if (use_gdb_syscalls()) { | |
256 | + gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len); | |
257 | + ret = env->regs[0]; | |
258 | + } else { | |
259 | + ret = write(STDERR_FILENO, s, len); | |
260 | + } | |
187 | 261 | unlock_user(s, args, 0); |
188 | 262 | return ret; |
189 | 263 | case SYS_WRITE: |
190 | 264 | len = ARG(2); |
191 | - s = lock_user(ARG(1), len, 1); | |
192 | - ret = set_swi_errno(ts, write(ARG(0), s, len)); | |
193 | - unlock_user(s, ARG(1), 0); | |
194 | - if (ret == (uint32_t)-1) | |
195 | - return -1; | |
196 | - return ARG(2) - ret; | |
265 | + if (use_gdb_syscalls()) { | |
266 | + arm_semi_syscall_len = len; | |
267 | + gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len); | |
268 | + return env->regs[0]; | |
269 | + } else { | |
270 | + s = lock_user(ARG(1), len, 1); | |
271 | + ret = set_swi_errno(ts, write(ARG(0), s, len)); | |
272 | + unlock_user(s, ARG(1), 0); | |
273 | + if (ret == (uint32_t)-1) | |
274 | + return -1; | |
275 | + return len - ret; | |
276 | + } | |
197 | 277 | case SYS_READ: |
198 | 278 | len = ARG(2); |
199 | - s = lock_user(ARG(1), len, 0); | |
200 | - do | |
201 | - ret = set_swi_errno(ts, read(ARG(0), s, len)); | |
202 | - while (ret == -1 && errno == EINTR); | |
203 | - unlock_user(s, ARG(1), len); | |
204 | - if (ret == (uint32_t)-1) | |
205 | - return -1; | |
206 | - return ARG(2) - ret; | |
279 | + if (use_gdb_syscalls()) { | |
280 | + arm_semi_syscall_len = len; | |
281 | + gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len); | |
282 | + return env->regs[0]; | |
283 | + } else { | |
284 | + s = lock_user(ARG(1), len, 0); | |
285 | + do | |
286 | + ret = set_swi_errno(ts, read(ARG(0), s, len)); | |
287 | + while (ret == -1 && errno == EINTR); | |
288 | + unlock_user(s, ARG(1), len); | |
289 | + if (ret == (uint32_t)-1) | |
290 | + return -1; | |
291 | + return len - ret; | |
292 | + } | |
207 | 293 | case SYS_READC: |
208 | 294 | /* XXX: Read from debug cosole. Not implemented. */ |
209 | 295 | return 0; |
210 | 296 | case SYS_ISTTY: |
211 | - return isatty(ARG(0)); | |
297 | + if (use_gdb_syscalls()) { | |
298 | + gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0)); | |
299 | + return env->regs[0]; | |
300 | + } else { | |
301 | + return isatty(ARG(0)); | |
302 | + } | |
212 | 303 | case SYS_SEEK: |
213 | - ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET)); | |
214 | - if (ret == (uint32_t)-1) | |
215 | - return -1; | |
216 | - return 0; | |
304 | + if (use_gdb_syscalls()) { | |
305 | + gdb_do_syscall(arm_semi_cb, "fseek,%x,%x,0", ARG(0), ARG(1)); | |
306 | + return env->regs[0]; | |
307 | + } else { | |
308 | + ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET)); | |
309 | + if (ret == (uint32_t)-1) | |
310 | + return -1; | |
311 | + return 0; | |
312 | + } | |
217 | 313 | case SYS_FLEN: |
218 | - { | |
314 | + if (use_gdb_syscalls()) { | |
315 | + /* TODO: Use stat syscall. */ | |
316 | + return -1; | |
317 | + } else { | |
219 | 318 | struct stat buf; |
220 | 319 | ret = set_swi_errno(ts, fstat(ARG(0), &buf)); |
221 | 320 | if (ret == (uint32_t)-1) |
... | ... | @@ -226,12 +325,21 @@ uint32_t do_arm_semihosting(CPUState *env) |
226 | 325 | /* XXX: Not implemented. */ |
227 | 326 | return -1; |
228 | 327 | case SYS_REMOVE: |
229 | - s = lock_user_string(ARG(0)); | |
230 | - ret = set_swi_errno(ts, remove(s)); | |
231 | - unlock_user(s, ARG(0), 0); | |
328 | + if (use_gdb_syscalls()) { | |
329 | + gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)); | |
330 | + ret = env->regs[0]; | |
331 | + } else { | |
332 | + s = lock_user_string(ARG(0)); | |
333 | + ret = set_swi_errno(ts, remove(s)); | |
334 | + unlock_user(s, ARG(0), 0); | |
335 | + } | |
232 | 336 | return ret; |
233 | 337 | case SYS_RENAME: |
234 | - { | |
338 | + if (use_gdb_syscalls()) { | |
339 | + gdb_do_syscall(arm_semi_cb, "rename,%s,%s", | |
340 | + ARG(0), (int)ARG(1), ARG(2), (int)ARG(3)); | |
341 | + return env->regs[0]; | |
342 | + } else { | |
235 | 343 | char *s2; |
236 | 344 | s = lock_user_string(ARG(0)); |
237 | 345 | s2 = lock_user_string(ARG(2)); |
... | ... | @@ -245,9 +353,14 @@ uint32_t do_arm_semihosting(CPUState *env) |
245 | 353 | case SYS_TIME: |
246 | 354 | return set_swi_errno(ts, time(NULL)); |
247 | 355 | case SYS_SYSTEM: |
248 | - s = lock_user_string(ARG(0)); | |
249 | - ret = set_swi_errno(ts, system(s)); | |
250 | - unlock_user(s, ARG(0), 0); | |
356 | + if (use_gdb_syscalls()) { | |
357 | + gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)); | |
358 | + return env->regs[0]; | |
359 | + } else { | |
360 | + s = lock_user_string(ARG(0)); | |
361 | + ret = set_swi_errno(ts, system(s)); | |
362 | + unlock_user(s, ARG(0), 0); | |
363 | + } | |
251 | 364 | case SYS_ERRNO: |
252 | 365 | #ifdef CONFIG_USER_ONLY |
253 | 366 | return ts->swi_errno; | ... | ... |
gdbstub.c
... | ... | @@ -52,6 +52,7 @@ enum RSState { |
52 | 52 | RS_GETLINE, |
53 | 53 | RS_CHKSUM1, |
54 | 54 | RS_CHKSUM2, |
55 | + RS_SYSCALL, | |
55 | 56 | }; |
56 | 57 | typedef struct GDBState { |
57 | 58 | CPUState *env; /* current CPU */ |
... | ... | @@ -96,6 +97,27 @@ static int get_char(GDBState *s) |
96 | 97 | } |
97 | 98 | #endif |
98 | 99 | |
100 | +/* GDB stub state for use by semihosting syscalls. */ | |
101 | +static GDBState *gdb_syscall_state; | |
102 | +static gdb_syscall_complete_cb gdb_current_syscall_cb; | |
103 | + | |
104 | +enum { | |
105 | + GDB_SYS_UNKNOWN, | |
106 | + GDB_SYS_ENABLED, | |
107 | + GDB_SYS_DISABLED, | |
108 | +} gdb_syscall_mode; | |
109 | + | |
110 | +/* If gdb is connected when the first semihosting syscall occurs then use | |
111 | + remote gdb syscalls. Otherwise use native file IO. */ | |
112 | +int use_gdb_syscalls(void) | |
113 | +{ | |
114 | + if (gdb_syscall_mode == GDB_SYS_UNKNOWN) { | |
115 | + gdb_syscall_mode = (gdb_syscall_state ? GDB_SYS_ENABLED | |
116 | + : GDB_SYS_DISABLED); | |
117 | + } | |
118 | + return gdb_syscall_mode == GDB_SYS_ENABLED; | |
119 | +} | |
120 | + | |
99 | 121 | static void put_buffer(GDBState *s, const uint8_t *buf, int len) |
100 | 122 | { |
101 | 123 | #ifdef CONFIG_USER_ONLY |
... | ... | @@ -755,6 +777,34 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf) |
755 | 777 | vm_start(); |
756 | 778 | #endif |
757 | 779 | return RS_IDLE; |
780 | + case 'F': | |
781 | + { | |
782 | + target_ulong ret; | |
783 | + target_ulong err; | |
784 | + | |
785 | + ret = strtoull(p, (char **)&p, 16); | |
786 | + if (*p == ',') { | |
787 | + p++; | |
788 | + err = strtoull(p, (char **)&p, 16); | |
789 | + } else { | |
790 | + err = 0; | |
791 | + } | |
792 | + if (*p == ',') | |
793 | + p++; | |
794 | + type = *p; | |
795 | + if (gdb_current_syscall_cb) | |
796 | + gdb_current_syscall_cb(s->env, ret, err); | |
797 | + if (type == 'C') { | |
798 | + put_packet(s, "T02"); | |
799 | + } else { | |
800 | +#ifdef CONFIG_USER_ONLY | |
801 | + s->running_state = 1; | |
802 | +#else | |
803 | + vm_start(); | |
804 | +#endif | |
805 | + } | |
806 | + } | |
807 | + break; | |
758 | 808 | case 'g': |
759 | 809 | reg_size = cpu_gdb_read_registers(env, mem_buf); |
760 | 810 | memtohex(buf, mem_buf, reg_size); |
... | ... | @@ -855,6 +905,9 @@ static void gdb_vm_stopped(void *opaque, int reason) |
855 | 905 | char buf[256]; |
856 | 906 | int ret; |
857 | 907 | |
908 | + if (s->state == RS_SYSCALL) | |
909 | + return; | |
910 | + | |
858 | 911 | /* disable single step if it was enable */ |
859 | 912 | cpu_single_step(s->env, 0); |
860 | 913 | |
... | ... | @@ -871,6 +924,60 @@ static void gdb_vm_stopped(void *opaque, int reason) |
871 | 924 | } |
872 | 925 | #endif |
873 | 926 | |
927 | +/* Send a gdb syscall request. | |
928 | + This accepts limited printf-style format specifiers, specifically: | |
929 | + %x - target_ulong argument printed in hex. | |
930 | + %s - string pointer (target_ulong) and length (int) pair. */ | |
931 | +void gdb_do_syscall(gdb_syscall_complete_cb cb, char *fmt, ...) | |
932 | +{ | |
933 | + va_list va; | |
934 | + char buf[256]; | |
935 | + char *p; | |
936 | + target_ulong addr; | |
937 | + GDBState *s; | |
938 | + | |
939 | + s = gdb_syscall_state; | |
940 | + if (!s) | |
941 | + return; | |
942 | + gdb_current_syscall_cb = cb; | |
943 | + s->state = RS_SYSCALL; | |
944 | +#ifndef CONFIG_USER_ONLY | |
945 | + vm_stop(EXCP_DEBUG); | |
946 | +#endif | |
947 | + s->state = RS_IDLE; | |
948 | + va_start(va, fmt); | |
949 | + p = buf; | |
950 | + *(p++) = 'F'; | |
951 | + while (*fmt) { | |
952 | + if (*fmt == '%') { | |
953 | + fmt++; | |
954 | + switch (*fmt++) { | |
955 | + case 'x': | |
956 | + addr = va_arg(va, target_ulong); | |
957 | + p += sprintf(p, TARGET_FMT_lx, addr); | |
958 | + break; | |
959 | + case 's': | |
960 | + addr = va_arg(va, target_ulong); | |
961 | + p += sprintf(p, TARGET_FMT_lx "/%x", addr, va_arg(va, int)); | |
962 | + break; | |
963 | + default: | |
964 | + fprintf(stderr, "gdbstub: Bad syscall format string '%s'\n", | |
965 | + fmt - 1); | |
966 | + break; | |
967 | + } | |
968 | + } else { | |
969 | + *(p++) = *(fmt++); | |
970 | + } | |
971 | + } | |
972 | + va_end(va); | |
973 | + put_packet(s, buf); | |
974 | +#ifdef CONFIG_USER_ONLY | |
975 | + gdb_handlesig(s->env, 0); | |
976 | +#else | |
977 | + cpu_interrupt(s->env, CPU_INTERRUPT_EXIT); | |
978 | +#endif | |
979 | +} | |
980 | + | |
874 | 981 | static void gdb_read_byte(GDBState *s, int ch) |
875 | 982 | { |
876 | 983 | CPUState *env = s->env; |
... | ... | @@ -942,6 +1049,8 @@ static void gdb_read_byte(GDBState *s, int ch) |
942 | 1049 | s->state = gdb_handle_packet(s, env, s->line_buf); |
943 | 1050 | } |
944 | 1051 | break; |
1052 | + default: | |
1053 | + abort(); | |
945 | 1054 | } |
946 | 1055 | } |
947 | 1056 | } |
... | ... | @@ -1034,6 +1143,8 @@ static void gdb_accept(void *opaque) |
1034 | 1143 | s->env = first_cpu; /* XXX: allow to change CPU */ |
1035 | 1144 | s->fd = fd; |
1036 | 1145 | |
1146 | + gdb_syscall_state = s; | |
1147 | + | |
1037 | 1148 | fcntl(fd, F_SETFL, O_NONBLOCK); |
1038 | 1149 | } |
1039 | 1150 | |
... | ... | @@ -1098,6 +1209,7 @@ static void gdb_chr_event(void *opaque, int event) |
1098 | 1209 | switch (event) { |
1099 | 1210 | case CHR_EVENT_RESET: |
1100 | 1211 | vm_stop(EXCP_INTERRUPT); |
1212 | + gdb_syscall_state = opaque; | |
1101 | 1213 | break; |
1102 | 1214 | default: |
1103 | 1215 | break; | ... | ... |
gdbstub.h
... | ... | @@ -3,6 +3,11 @@ |
3 | 3 | |
4 | 4 | #define DEFAULT_GDBSTUB_PORT 1234 |
5 | 5 | |
6 | +typedef void (*gdb_syscall_complete_cb)(CPUState *env, | |
7 | + target_ulong ret, target_ulong err); | |
8 | + | |
9 | +void gdb_do_syscall(gdb_syscall_complete_cb cb, char *fmt, ...); | |
10 | +int use_gdb_syscalls(void); | |
6 | 11 | #ifdef CONFIG_USER_ONLY |
7 | 12 | int gdb_handlesig (CPUState *, int); |
8 | 13 | void gdb_exit(CPUState *, int); | ... | ... |