Commit 9307c4c1d93939db9b04117b654253af5113dc21
1 parent
40c3bac3
improved monitor: type check, expression evaluator, memory dump, disassembly
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@703 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
843 additions
and
121 deletions
disas.c
1 | 1 | /* General "disassemble this chunk" code. Used for debugging. */ |
2 | 2 | #include "config.h" |
3 | 3 | #include "dis-asm.h" |
4 | -#include "disas.h" | |
5 | 4 | #include "elf.h" |
6 | 5 | #include <errno.h> |
7 | 6 | |
8 | 7 | #include "cpu.h" |
9 | 8 | #include "exec-all.h" |
9 | +#include "disas.h" | |
10 | 10 | |
11 | 11 | /* Filled in by elfload.c. Simplistic, but will do for now. */ |
12 | 12 | unsigned int disas_num_syms; |
... | ... | @@ -219,3 +219,71 @@ const char *lookup_symbol(void *orig_addr) |
219 | 219 | } |
220 | 220 | return ""; |
221 | 221 | } |
222 | + | |
223 | +#if !defined(CONFIG_USER_ONLY) | |
224 | + | |
225 | +static int monitor_disas_is_physical; | |
226 | + | |
227 | +static int | |
228 | +monitor_read_memory (memaddr, myaddr, length, info) | |
229 | + bfd_vma memaddr; | |
230 | + bfd_byte *myaddr; | |
231 | + int length; | |
232 | + struct disassemble_info *info; | |
233 | +{ | |
234 | + if (monitor_disas_is_physical) { | |
235 | + cpu_physical_memory_rw(memaddr, myaddr, length, 0); | |
236 | + } else { | |
237 | + cpu_memory_rw_debug(cpu_single_env, memaddr,myaddr, length, 0); | |
238 | + } | |
239 | + return 0; | |
240 | +} | |
241 | + | |
242 | +void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags) | |
243 | +{ | |
244 | + FILE *out; | |
245 | + int count, i; | |
246 | + struct disassemble_info disasm_info; | |
247 | + int (*print_insn)(bfd_vma pc, disassemble_info *info); | |
248 | + | |
249 | + out = stdout; | |
250 | + | |
251 | + INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf); | |
252 | + | |
253 | + monitor_disas_is_physical = is_physical; | |
254 | + disasm_info.read_memory_func = monitor_read_memory; | |
255 | + | |
256 | + disasm_info.buffer_vma = pc; | |
257 | + | |
258 | +#ifdef TARGET_WORDS_BIGENDIAN | |
259 | + disasm_info.endian = BFD_ENDIAN_BIG; | |
260 | +#else | |
261 | + disasm_info.endian = BFD_ENDIAN_LITTLE; | |
262 | +#endif | |
263 | +#if defined(TARGET_I386) | |
264 | + if (!flags) | |
265 | + disasm_info.mach = bfd_mach_i386_i386; | |
266 | + else | |
267 | + disasm_info.mach = bfd_mach_i386_i8086; | |
268 | + print_insn = print_insn_i386; | |
269 | +#elif defined(TARGET_ARM) | |
270 | + print_insn = print_insn_arm; | |
271 | +#elif defined(TARGET_SPARC) | |
272 | + print_insn = print_insn_sparc; | |
273 | +#elif defined(TARGET_PPC) | |
274 | + print_insn = print_insn_ppc; | |
275 | +#else | |
276 | + fprintf(out, "Asm output not supported on this arch\n"); | |
277 | + return; | |
278 | +#endif | |
279 | + | |
280 | + for(i = 0; i < nb_insn; i++) { | |
281 | + fprintf(out, "0x%08lx: ", (unsigned long)pc); | |
282 | + count = print_insn(pc, &disasm_info); | |
283 | + fprintf(out, "\n"); | |
284 | + if (count < 0) | |
285 | + break; | |
286 | + pc += count; | |
287 | + } | |
288 | +} | |
289 | +#endif | ... | ... |
disas.h
... | ... | @@ -3,6 +3,7 @@ |
3 | 3 | |
4 | 4 | /* Disassemble this for me please... (debugging). */ |
5 | 5 | void disas(FILE *out, void *code, unsigned long size, int is_host, int flags); |
6 | +void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags); | |
6 | 7 | |
7 | 8 | /* Look up symbol for debugging purpose. Returns "" if unknown. */ |
8 | 9 | const char *lookup_symbol(void *orig_addr); | ... | ... |
monitor.c
... | ... | @@ -22,11 +22,15 @@ |
22 | 22 | * THE SOFTWARE. |
23 | 23 | */ |
24 | 24 | #include "vl.h" |
25 | +#include "disas.h" | |
25 | 26 | |
26 | 27 | //#define DEBUG |
27 | 28 | |
29 | +#ifndef offsetof | |
30 | +#define offsetof(type, field) ((size_t) &((type *)0)->field) | |
31 | +#endif | |
32 | + | |
28 | 33 | #define TERM_CMD_BUF_SIZE 4095 |
29 | -#define MAX_ARGS 64 | |
30 | 34 | |
31 | 35 | #define IS_NORM 0 |
32 | 36 | #define IS_ESC 1 |
... | ... | @@ -40,9 +44,22 @@ static int term_cmd_buf_size; |
40 | 44 | static int term_esc_state; |
41 | 45 | static int term_esc_param; |
42 | 46 | |
47 | +/* | |
48 | + * Supported types: | |
49 | + * | |
50 | + * 'F' filename | |
51 | + * 's' string (accept optional quote) | |
52 | + * 'i' integer | |
53 | + * '/' optional gdb-like print format (like "/10x") | |
54 | + * | |
55 | + * '?' optional type (for 'F', 's' and 'i') | |
56 | + * | |
57 | + */ | |
58 | + | |
43 | 59 | typedef struct term_cmd_t { |
44 | 60 | const char *name; |
45 | - void (*handler)(int argc, const char **argv); | |
61 | + const char *args_type; | |
62 | + void (*handler)(); | |
46 | 63 | const char *params; |
47 | 64 | const char *help; |
48 | 65 | } term_cmd_t; |
... | ... | @@ -110,12 +127,12 @@ static void help_cmd(const char *name) |
110 | 127 | } |
111 | 128 | } |
112 | 129 | |
113 | -static void do_help(int argc, const char **argv) | |
130 | +static void do_help(const char *name) | |
114 | 131 | { |
115 | - help_cmd(argv[1]); | |
132 | + help_cmd(name); | |
116 | 133 | } |
117 | 134 | |
118 | -static void do_commit(int argc, const char **argv) | |
135 | +static void do_commit(void) | |
119 | 136 | { |
120 | 137 | int i; |
121 | 138 | |
... | ... | @@ -125,26 +142,24 @@ static void do_commit(int argc, const char **argv) |
125 | 142 | } |
126 | 143 | } |
127 | 144 | |
128 | -static void do_info(int argc, const char **argv) | |
145 | +static void do_info(const char *item) | |
129 | 146 | { |
130 | 147 | term_cmd_t *cmd; |
131 | - const char *item; | |
132 | 148 | |
133 | - if (argc < 2) | |
149 | + if (!item) | |
134 | 150 | goto help; |
135 | - item = argv[1]; | |
136 | 151 | for(cmd = info_cmds; cmd->name != NULL; cmd++) { |
137 | - if (compare_cmd(argv[1], cmd->name)) | |
152 | + if (compare_cmd(item, cmd->name)) | |
138 | 153 | goto found; |
139 | 154 | } |
140 | 155 | help: |
141 | - help_cmd(argv[0]); | |
156 | + help_cmd("info"); | |
142 | 157 | return; |
143 | 158 | found: |
144 | - cmd->handler(argc, argv); | |
159 | + cmd->handler(); | |
145 | 160 | } |
146 | 161 | |
147 | -static void do_info_network(int argc, const char **argv) | |
162 | +static void do_info_network(void) | |
148 | 163 | { |
149 | 164 | int i, j; |
150 | 165 | NetDriverState *nd; |
... | ... | @@ -161,12 +176,21 @@ static void do_info_network(int argc, const char **argv) |
161 | 176 | } |
162 | 177 | } |
163 | 178 | |
164 | -static void do_info_block(int argc, const char **argv) | |
179 | +static void do_info_block(void) | |
165 | 180 | { |
166 | 181 | bdrv_info(); |
167 | 182 | } |
168 | 183 | |
169 | -static void do_quit(int argc, const char **argv) | |
184 | +static void do_info_registers(void) | |
185 | +{ | |
186 | +#ifdef TARGET_I386 | |
187 | + cpu_dump_state(cpu_single_env, stdout, X86_DUMP_FPU | X86_DUMP_CCOP); | |
188 | +#else | |
189 | + cpu_dump_state(cpu_single_env, stdout, 0); | |
190 | +#endif | |
191 | +} | |
192 | + | |
193 | +static void do_quit(void) | |
170 | 194 | { |
171 | 195 | exit(0); |
172 | 196 | } |
... | ... | @@ -189,26 +213,13 @@ static int eject_device(BlockDriverState *bs, int force) |
189 | 213 | return 0; |
190 | 214 | } |
191 | 215 | |
192 | -static void do_eject(int argc, const char **argv) | |
216 | +static void do_eject(int force, const char *filename) | |
193 | 217 | { |
194 | 218 | BlockDriverState *bs; |
195 | - const char **parg; | |
196 | - int force; | |
197 | 219 | |
198 | - parg = argv + 1; | |
199 | - if (!*parg) { | |
200 | - fail: | |
201 | - help_cmd(argv[0]); | |
202 | - return; | |
203 | - } | |
204 | - force = 0; | |
205 | - if (!strcmp(*parg, "-f")) { | |
206 | - force = 1; | |
207 | - parg++; | |
208 | - } | |
209 | - if (!*parg) | |
210 | - goto fail; | |
211 | - bs = bdrv_find(*parg); | |
220 | + term_printf("%d %s\n", force, filename); | |
221 | + | |
222 | + bs = bdrv_find(filename); | |
212 | 223 | if (!bs) { |
213 | 224 | term_printf("device not found\n"); |
214 | 225 | return; |
... | ... | @@ -216,90 +227,68 @@ static void do_eject(int argc, const char **argv) |
216 | 227 | eject_device(bs, force); |
217 | 228 | } |
218 | 229 | |
219 | -static void do_change(int argc, const char **argv) | |
230 | +static void do_change(const char *device, const char *filename) | |
220 | 231 | { |
221 | 232 | BlockDriverState *bs; |
222 | 233 | |
223 | - if (argc != 3) { | |
224 | - help_cmd(argv[0]); | |
225 | - return; | |
226 | - } | |
227 | - bs = bdrv_find(argv[1]); | |
234 | + bs = bdrv_find(device); | |
228 | 235 | if (!bs) { |
229 | 236 | term_printf("device not found\n"); |
230 | 237 | return; |
231 | 238 | } |
232 | 239 | if (eject_device(bs, 0) < 0) |
233 | 240 | return; |
234 | - bdrv_open(bs, argv[2], 0); | |
241 | + bdrv_open(bs, filename, 0); | |
235 | 242 | } |
236 | 243 | |
237 | -static void do_screen_dump(int argc, const char **argv) | |
244 | +static void do_screen_dump(const char *filename) | |
238 | 245 | { |
239 | - if (argc != 2) { | |
240 | - help_cmd(argv[0]); | |
241 | - return; | |
242 | - } | |
243 | - vga_screen_dump(argv[1]); | |
246 | + vga_screen_dump(filename); | |
244 | 247 | } |
245 | 248 | |
246 | -static void do_log(int argc, const char **argv) | |
249 | +static void do_log(const char *items) | |
247 | 250 | { |
248 | 251 | int mask; |
249 | 252 | |
250 | - if (argc != 2) | |
251 | - goto help; | |
252 | - if (!strcmp(argv[1], "none")) { | |
253 | + if (!strcmp(items, "none")) { | |
253 | 254 | mask = 0; |
254 | 255 | } else { |
255 | - mask = cpu_str_to_log_mask(argv[1]); | |
256 | + mask = cpu_str_to_log_mask(items); | |
256 | 257 | if (!mask) { |
257 | - help: | |
258 | - help_cmd(argv[0]); | |
258 | + help_cmd("log"); | |
259 | 259 | return; |
260 | 260 | } |
261 | 261 | } |
262 | 262 | cpu_set_log(mask); |
263 | 263 | } |
264 | 264 | |
265 | -static void do_savevm(int argc, const char **argv) | |
265 | +static void do_savevm(const char *filename) | |
266 | 266 | { |
267 | - if (argc != 2) { | |
268 | - help_cmd(argv[0]); | |
269 | - return; | |
270 | - } | |
271 | - if (qemu_savevm(argv[1]) < 0) | |
272 | - term_printf("I/O error when saving VM to '%s'\n", argv[1]); | |
267 | + if (qemu_savevm(filename) < 0) | |
268 | + term_printf("I/O error when saving VM to '%s'\n", filename); | |
273 | 269 | } |
274 | 270 | |
275 | -static void do_loadvm(int argc, const char **argv) | |
271 | +static void do_loadvm(const char *filename) | |
276 | 272 | { |
277 | - if (argc != 2) { | |
278 | - help_cmd(argv[0]); | |
279 | - return; | |
280 | - } | |
281 | - if (qemu_loadvm(argv[1]) < 0) | |
282 | - term_printf("I/O error when loading VM from '%s'\n", argv[1]); | |
273 | + if (qemu_loadvm(filename) < 0) | |
274 | + term_printf("I/O error when loading VM from '%s'\n", filename); | |
283 | 275 | } |
284 | 276 | |
285 | -static void do_stop(int argc, const char **argv) | |
277 | +static void do_stop(void) | |
286 | 278 | { |
287 | 279 | vm_stop(EXCP_INTERRUPT); |
288 | 280 | } |
289 | 281 | |
290 | -static void do_cont(int argc, const char **argv) | |
282 | +static void do_cont(void) | |
291 | 283 | { |
292 | 284 | vm_start(); |
293 | 285 | } |
294 | 286 | |
295 | 287 | #ifdef CONFIG_GDBSTUB |
296 | -static void do_gdbserver(int argc, const char **argv) | |
288 | +static void do_gdbserver(int has_port, int port) | |
297 | 289 | { |
298 | - int port; | |
299 | - | |
300 | - port = DEFAULT_GDBSTUB_PORT; | |
301 | - if (argc >= 2) | |
302 | - port = atoi(argv[1]); | |
290 | + if (!has_port) | |
291 | + port = DEFAULT_GDBSTUB_PORT; | |
303 | 292 | if (gdbserver_start(port) < 0) { |
304 | 293 | qemu_printf("Could not open gdbserver socket on port %d\n", port); |
305 | 294 | } else { |
... | ... | @@ -308,90 +297,754 @@ static void do_gdbserver(int argc, const char **argv) |
308 | 297 | } |
309 | 298 | #endif |
310 | 299 | |
300 | +static void term_printc(int c) | |
301 | +{ | |
302 | + term_printf("'"); | |
303 | + switch(c) { | |
304 | + case '\'': | |
305 | + term_printf("\\'"); | |
306 | + break; | |
307 | + case '\\': | |
308 | + term_printf("\\\\"); | |
309 | + break; | |
310 | + case '\n': | |
311 | + term_printf("\\n"); | |
312 | + break; | |
313 | + case '\r': | |
314 | + term_printf("\\r"); | |
315 | + break; | |
316 | + default: | |
317 | + if (c >= 32 && c <= 126) { | |
318 | + term_printf("%c", c); | |
319 | + } else { | |
320 | + term_printf("\\x%02x", c); | |
321 | + } | |
322 | + break; | |
323 | + } | |
324 | + term_printf("'"); | |
325 | +} | |
326 | + | |
327 | +static void memory_dump(int count, int format, int wsize, | |
328 | + target_ulong addr, int is_physical) | |
329 | +{ | |
330 | + int nb_per_line, l, line_size, i, max_digits, len; | |
331 | + uint8_t buf[16]; | |
332 | + uint64_t v; | |
333 | + | |
334 | + if (format == 'i') { | |
335 | + int flags; | |
336 | + flags = 0; | |
337 | +#ifdef TARGET_I386 | |
338 | + /* we use the current CS size */ | |
339 | + if (!(cpu_single_env->segs[R_CS].flags & DESC_B_MASK)) | |
340 | + flags = 1; | |
341 | +#endif | |
342 | + monitor_disas(addr, count, is_physical, flags); | |
343 | + return; | |
344 | + } | |
345 | + | |
346 | + len = wsize * count; | |
347 | + if (wsize == 1) | |
348 | + line_size = 8; | |
349 | + else | |
350 | + line_size = 16; | |
351 | + nb_per_line = line_size / wsize; | |
352 | + max_digits = 0; | |
353 | + | |
354 | + switch(format) { | |
355 | + case 'o': | |
356 | + max_digits = (wsize * 8 + 2) / 3; | |
357 | + break; | |
358 | + default: | |
359 | + case 'x': | |
360 | + max_digits = (wsize * 8) / 4; | |
361 | + break; | |
362 | + case 'u': | |
363 | + case 'd': | |
364 | + max_digits = (wsize * 8 * 10 + 32) / 33; | |
365 | + break; | |
366 | + case 'c': | |
367 | + wsize = 1; | |
368 | + break; | |
369 | + } | |
370 | + | |
371 | + while (len > 0) { | |
372 | + term_printf("0x%08x:", addr); | |
373 | + l = len; | |
374 | + if (l > line_size) | |
375 | + l = line_size; | |
376 | + if (is_physical) { | |
377 | + cpu_physical_memory_rw(addr, buf, l, 0); | |
378 | + } else { | |
379 | + cpu_memory_rw_debug(cpu_single_env, addr, buf, l, 0); | |
380 | + } | |
381 | + i = 0; | |
382 | + while (i < l) { | |
383 | + switch(wsize) { | |
384 | + default: | |
385 | + case 1: | |
386 | + v = ldub_raw(buf + i); | |
387 | + break; | |
388 | + case 2: | |
389 | + v = lduw_raw(buf + i); | |
390 | + break; | |
391 | + case 4: | |
392 | + v = ldl_raw(buf + i); | |
393 | + break; | |
394 | + case 8: | |
395 | + v = ldq_raw(buf + i); | |
396 | + break; | |
397 | + } | |
398 | + term_printf(" "); | |
399 | + switch(format) { | |
400 | + case 'o': | |
401 | + term_printf("%#*llo", max_digits, v); | |
402 | + break; | |
403 | + case 'x': | |
404 | + term_printf("0x%0*llx", max_digits, v); | |
405 | + break; | |
406 | + case 'u': | |
407 | + term_printf("%*llu", max_digits, v); | |
408 | + break; | |
409 | + case 'd': | |
410 | + term_printf("%*lld", max_digits, v); | |
411 | + break; | |
412 | + case 'c': | |
413 | + term_printc(v); | |
414 | + break; | |
415 | + } | |
416 | + i += wsize; | |
417 | + } | |
418 | + term_printf("\n"); | |
419 | + addr += l; | |
420 | + len -= l; | |
421 | + } | |
422 | +} | |
423 | + | |
424 | +static void do_memory_dump(int count, int format, int size, int addr) | |
425 | +{ | |
426 | + memory_dump(count, format, size, addr, 0); | |
427 | +} | |
428 | + | |
429 | +static void do_physical_memory_dump(int count, int format, int size, int addr) | |
430 | +{ | |
431 | + memory_dump(count, format, size, addr, 1); | |
432 | +} | |
433 | + | |
434 | +static void do_print(int count, int format, int size, int val) | |
435 | +{ | |
436 | + switch(format) { | |
437 | + case 'o': | |
438 | + term_printf("%#o", val); | |
439 | + break; | |
440 | + case 'x': | |
441 | + term_printf("%#x", val); | |
442 | + break; | |
443 | + case 'u': | |
444 | + term_printf("%u", val); | |
445 | + break; | |
446 | + default: | |
447 | + case 'd': | |
448 | + term_printf("%d", val); | |
449 | + break; | |
450 | + case 'c': | |
451 | + term_printc(val); | |
452 | + break; | |
453 | + } | |
454 | + term_printf("\n"); | |
455 | +} | |
456 | + | |
311 | 457 | static term_cmd_t term_cmds[] = { |
312 | - { "help|?", do_help, | |
458 | + { "help|?", "s?", do_help, | |
313 | 459 | "[cmd]", "show the help" }, |
314 | - { "commit", do_commit, | |
460 | + { "commit", "", do_commit, | |
315 | 461 | "", "commit changes to the disk images (if -snapshot is used)" }, |
316 | - { "info", do_info, | |
462 | + { "info", "s?", do_info, | |
317 | 463 | "subcommand", "show various information about the system state" }, |
318 | - { "q|quit", do_quit, | |
464 | + { "q|quit", "", do_quit, | |
319 | 465 | "", "quit the emulator" }, |
320 | - { "eject", do_eject, | |
466 | + { "eject", "-fs", do_eject, | |
321 | 467 | "[-f] device", "eject a removable media (use -f to force it)" }, |
322 | - { "change", do_change, | |
468 | + { "change", "sF", do_change, | |
323 | 469 | "device filename", "change a removable media" }, |
324 | - { "screendump", do_screen_dump, | |
470 | + { "screendump", "F", do_screen_dump, | |
325 | 471 | "filename", "save screen into PPM image 'filename'" }, |
326 | - { "log", do_log, | |
472 | + { "log", "s", do_log, | |
327 | 473 | "item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" }, |
328 | - { "savevm", do_savevm, | |
474 | + { "savevm", "F", do_savevm, | |
329 | 475 | "filename", "save the whole virtual machine state to 'filename'" }, |
330 | - { "loadvm", do_loadvm, | |
476 | + { "loadvm", "F", do_loadvm, | |
331 | 477 | "filename", "restore the whole virtual machine state from 'filename'" }, |
332 | - { "stop", do_stop, "", "stop emulation", }, | |
333 | - { "c|cont", do_cont, "", "resume emulation", }, | |
478 | + { "stop", "", do_stop, | |
479 | + "", "stop emulation", }, | |
480 | + { "c|cont", "", do_cont, | |
481 | + "", "resume emulation", }, | |
334 | 482 | #ifdef CONFIG_GDBSTUB |
335 | - { "gdbserver", do_gdbserver, "[port]", "start gdbserver session (default port=1234)", }, | |
483 | + { "gdbserver", "i?", do_gdbserver, | |
484 | + "[port]", "start gdbserver session (default port=1234)", }, | |
336 | 485 | #endif |
486 | + { "x", "/i", do_memory_dump, | |
487 | + "/fmt addr", "virtual memory dump starting at 'addr'", }, | |
488 | + { "xp", "/i", do_physical_memory_dump, | |
489 | + "/fmt addr", "physical memory dump starting at 'addr'", }, | |
490 | + { "p|print", "/i", do_print, | |
491 | + "/fmt expr", "print expression value (use $reg for CPU register access)", }, | |
337 | 492 | { NULL, NULL, }, |
338 | 493 | }; |
339 | 494 | |
340 | 495 | static term_cmd_t info_cmds[] = { |
341 | - { "network", do_info_network, | |
496 | + { "network", "", do_info_network, | |
342 | 497 | "", "show the network state" }, |
343 | - { "block", do_info_block, | |
498 | + { "block", "", do_info_block, | |
344 | 499 | "", "show the block devices" }, |
500 | + { "registers", "", do_info_registers, | |
501 | + "", "show the cpu registers" }, | |
345 | 502 | { NULL, NULL, }, |
346 | 503 | }; |
347 | 504 | |
348 | -static void term_handle_command(char *cmdline) | |
505 | +/*******************************************************************/ | |
506 | + | |
507 | +static const char *pch; | |
508 | +static jmp_buf expr_env; | |
509 | + | |
510 | +typedef struct MonitorDef { | |
511 | + const char *name; | |
512 | + int offset; | |
513 | + int (*get_value)(struct MonitorDef *md); | |
514 | +} MonitorDef; | |
515 | + | |
516 | +static MonitorDef monitor_defs[] = { | |
517 | +#ifdef TARGET_I386 | |
518 | + { "eax", offsetof(CPUState, regs[0]) }, | |
519 | + { "ecx", offsetof(CPUState, regs[1]) }, | |
520 | + { "edx", offsetof(CPUState, regs[2]) }, | |
521 | + { "ebx", offsetof(CPUState, regs[3]) }, | |
522 | + { "esp|sp", offsetof(CPUState, regs[4]) }, | |
523 | + { "ebp|fp", offsetof(CPUState, regs[5]) }, | |
524 | + { "esi", offsetof(CPUState, regs[6]) }, | |
525 | + { "esi", offsetof(CPUState, regs[7]) }, | |
526 | + { "eflags", offsetof(CPUState, eflags) }, | |
527 | + { "eip|pc", offsetof(CPUState, eip) }, | |
528 | +#endif | |
529 | + { NULL }, | |
530 | +}; | |
531 | + | |
532 | +static void expr_error(const char *fmt) | |
533 | +{ | |
534 | + term_printf(fmt); | |
535 | + term_printf("\n"); | |
536 | + longjmp(expr_env, 1); | |
537 | +} | |
538 | + | |
539 | +static int get_monitor_def(int *pval, const char *name) | |
540 | +{ | |
541 | + MonitorDef *md; | |
542 | + for(md = monitor_defs; md->name != NULL; md++) { | |
543 | + if (compare_cmd(name, md->name)) { | |
544 | + if (md->get_value) { | |
545 | + *pval = md->get_value(md); | |
546 | + } else { | |
547 | + *pval = *(uint32_t *)((uint8_t *)cpu_single_env + md->offset); | |
548 | + } | |
549 | + return 0; | |
550 | + } | |
551 | + } | |
552 | + return -1; | |
553 | +} | |
554 | + | |
555 | +static void next(void) | |
556 | +{ | |
557 | + if (pch != '\0') { | |
558 | + pch++; | |
559 | + while (isspace(*pch)) | |
560 | + pch++; | |
561 | + } | |
562 | +} | |
563 | + | |
564 | +static int expr_sum(void); | |
565 | + | |
566 | +static int expr_unary(void) | |
567 | +{ | |
568 | + int n; | |
569 | + char *p; | |
570 | + | |
571 | + switch(*pch) { | |
572 | + case '+': | |
573 | + next(); | |
574 | + n = expr_unary(); | |
575 | + break; | |
576 | + case '-': | |
577 | + next(); | |
578 | + n = -expr_unary(); | |
579 | + break; | |
580 | + case '~': | |
581 | + next(); | |
582 | + n = ~expr_unary(); | |
583 | + break; | |
584 | + case '(': | |
585 | + next(); | |
586 | + n = expr_sum(); | |
587 | + if (*pch != ')') { | |
588 | + expr_error("')' expected"); | |
589 | + } | |
590 | + next(); | |
591 | + break; | |
592 | + case '$': | |
593 | + { | |
594 | + char buf[128], *q; | |
595 | + | |
596 | + pch++; | |
597 | + q = buf; | |
598 | + while ((*pch >= 'a' && *pch <= 'z') || | |
599 | + (*pch >= 'A' && *pch <= 'Z') || | |
600 | + (*pch >= '0' && *pch <= '9') || | |
601 | + *pch == '_') { | |
602 | + if ((q - buf) < sizeof(buf) - 1) | |
603 | + *q++ = *pch; | |
604 | + pch++; | |
605 | + } | |
606 | + while (isspace(*pch)) | |
607 | + pch++; | |
608 | + *q = 0; | |
609 | + if (get_monitor_def(&n, buf)) | |
610 | + expr_error("unknown register"); | |
611 | + } | |
612 | + break; | |
613 | + case '\0': | |
614 | + expr_error("unexpected end of expression"); | |
615 | + n = 0; | |
616 | + break; | |
617 | + default: | |
618 | + n = strtoul(pch, &p, 0); | |
619 | + if (pch == p) { | |
620 | + expr_error("invalid char in expression"); | |
621 | + } | |
622 | + pch = p; | |
623 | + while (isspace(*pch)) | |
624 | + pch++; | |
625 | + break; | |
626 | + } | |
627 | + return n; | |
628 | +} | |
629 | + | |
630 | + | |
631 | +static int expr_prod(void) | |
632 | +{ | |
633 | + int val, val2, op; | |
634 | + | |
635 | + val = expr_unary(); | |
636 | + for(;;) { | |
637 | + op = *pch; | |
638 | + if (op != '*' && op != '/' && op != '%') | |
639 | + break; | |
640 | + next(); | |
641 | + val2 = expr_unary(); | |
642 | + switch(op) { | |
643 | + default: | |
644 | + case '*': | |
645 | + val *= val2; | |
646 | + break; | |
647 | + case '/': | |
648 | + case '%': | |
649 | + if (val2 == 0) | |
650 | + expr_error("divison by zero"); | |
651 | + if (op == '/') | |
652 | + val /= val2; | |
653 | + else | |
654 | + val %= val2; | |
655 | + break; | |
656 | + } | |
657 | + } | |
658 | + return val; | |
659 | +} | |
660 | + | |
661 | +static int expr_logic(void) | |
662 | +{ | |
663 | + int val, val2, op; | |
664 | + | |
665 | + val = expr_prod(); | |
666 | + for(;;) { | |
667 | + op = *pch; | |
668 | + if (op != '&' && op != '|' && op != '^') | |
669 | + break; | |
670 | + next(); | |
671 | + val2 = expr_prod(); | |
672 | + switch(op) { | |
673 | + default: | |
674 | + case '&': | |
675 | + val &= val2; | |
676 | + break; | |
677 | + case '|': | |
678 | + val |= val2; | |
679 | + break; | |
680 | + case '^': | |
681 | + val ^= val2; | |
682 | + break; | |
683 | + } | |
684 | + } | |
685 | + return val; | |
686 | +} | |
687 | + | |
688 | +static int expr_sum(void) | |
349 | 689 | { |
350 | - char *p, *pstart; | |
351 | - int argc; | |
352 | - const char *args[MAX_ARGS + 1]; | |
690 | + int val, val2, op; | |
691 | + | |
692 | + val = expr_logic(); | |
693 | + for(;;) { | |
694 | + op = *pch; | |
695 | + if (op != '+' && op != '-') | |
696 | + break; | |
697 | + next(); | |
698 | + val2 = expr_logic(); | |
699 | + if (op == '+') | |
700 | + val += val2; | |
701 | + else | |
702 | + val -= val2; | |
703 | + } | |
704 | + return val; | |
705 | +} | |
706 | + | |
707 | +static int get_expr(int *pval, const char **pp) | |
708 | +{ | |
709 | + pch = *pp; | |
710 | + if (setjmp(expr_env)) { | |
711 | + *pp = pch; | |
712 | + return -1; | |
713 | + } | |
714 | + while (isspace(*pch)) | |
715 | + pch++; | |
716 | + *pval = expr_sum(); | |
717 | + *pp = pch; | |
718 | + return 0; | |
719 | +} | |
720 | + | |
721 | +static int get_str(char *buf, int buf_size, const char **pp) | |
722 | +{ | |
723 | + const char *p; | |
724 | + char *q; | |
725 | + int c; | |
726 | + | |
727 | + p = *pp; | |
728 | + while (isspace(*p)) | |
729 | + p++; | |
730 | + if (*p == '\0') { | |
731 | + fail: | |
732 | + *pp = p; | |
733 | + return -1; | |
734 | + } | |
735 | + q = buf; | |
736 | + if (*p == '\"') { | |
737 | + p++; | |
738 | + while (*p != '\0' && *p != '\"') { | |
739 | + if (*p == '\\') { | |
740 | + p++; | |
741 | + c = *p++; | |
742 | + switch(c) { | |
743 | + case 'n': | |
744 | + c = '\n'; | |
745 | + break; | |
746 | + case 'r': | |
747 | + c = '\r'; | |
748 | + break; | |
749 | + case '\\': | |
750 | + case '\'': | |
751 | + case '\"': | |
752 | + break; | |
753 | + default: | |
754 | + qemu_printf("unsupported escape code: '\\%c'\n", c); | |
755 | + goto fail; | |
756 | + } | |
757 | + if ((q - buf) < buf_size - 1) { | |
758 | + *q++ = c; | |
759 | + } | |
760 | + } else { | |
761 | + if ((q - buf) < buf_size - 1) { | |
762 | + *q++ = *p; | |
763 | + } | |
764 | + p++; | |
765 | + } | |
766 | + } | |
767 | + if (*p != '\"') { | |
768 | + qemu_printf("untermintated string\n"); | |
769 | + goto fail; | |
770 | + } | |
771 | + p++; | |
772 | + } else { | |
773 | + while (*p != '\0' && !isspace(*p)) { | |
774 | + if ((q - buf) < buf_size - 1) { | |
775 | + *q++ = *p; | |
776 | + } | |
777 | + p++; | |
778 | + } | |
779 | + *q = '\0'; | |
780 | + } | |
781 | + *pp = p; | |
782 | + return 0; | |
783 | +} | |
784 | + | |
785 | +static int default_fmt_format = 'x'; | |
786 | +static int default_fmt_size = 4; | |
787 | + | |
788 | +#define MAX_ARGS 16 | |
789 | + | |
790 | +static void term_handle_command(const char *cmdline) | |
791 | +{ | |
792 | + const char *p, *pstart, *typestr; | |
793 | + char *q; | |
794 | + int c, nb_args, len, i, has_arg; | |
353 | 795 | term_cmd_t *cmd; |
796 | + char cmdname[256]; | |
797 | + char buf[1024]; | |
798 | + void *str_allocated[MAX_ARGS]; | |
799 | + void *args[MAX_ARGS]; | |
354 | 800 | |
355 | 801 | #ifdef DEBUG |
356 | 802 | term_printf("command='%s'\n", cmdline); |
357 | 803 | #endif |
358 | 804 | |
359 | - /* split command in words */ | |
360 | - argc = 0; | |
805 | + /* extract the command name */ | |
361 | 806 | p = cmdline; |
807 | + q = cmdname; | |
808 | + while (isspace(*p)) | |
809 | + p++; | |
810 | + if (*p == '\0') | |
811 | + return; | |
812 | + pstart = p; | |
813 | + while (*p != '\0' && *p != '/' && !isspace(*p)) | |
814 | + p++; | |
815 | + len = p - pstart; | |
816 | + if (len > sizeof(cmdname) - 1) | |
817 | + len = sizeof(cmdname) - 1; | |
818 | + memcpy(cmdname, pstart, len); | |
819 | + cmdname[len] = '\0'; | |
820 | + | |
821 | + /* find the command */ | |
822 | + for(cmd = term_cmds; cmd->name != NULL; cmd++) { | |
823 | + if (compare_cmd(cmdname, cmd->name)) | |
824 | + goto found; | |
825 | + } | |
826 | + term_printf("unknown command: '%s'\n", cmdname); | |
827 | + return; | |
828 | + found: | |
829 | + | |
830 | + for(i = 0; i < MAX_ARGS; i++) | |
831 | + str_allocated[i] = NULL; | |
832 | + | |
833 | + /* parse the parameters */ | |
834 | + typestr = cmd->args_type; | |
835 | + nb_args = 0; | |
362 | 836 | for(;;) { |
363 | - while (isspace(*p)) | |
364 | - p++; | |
365 | - if (*p == '\0') | |
837 | + c = *typestr; | |
838 | + if (c == '\0') | |
366 | 839 | break; |
367 | - pstart = p; | |
368 | - while (*p != '\0' && !isspace(*p)) | |
369 | - p++; | |
370 | - args[argc] = pstart; | |
371 | - argc++; | |
372 | - if (argc >= MAX_ARGS) | |
840 | + typestr++; | |
841 | + switch(c) { | |
842 | + case 'F': | |
843 | + case 's': | |
844 | + { | |
845 | + int ret; | |
846 | + char *str; | |
847 | + | |
848 | + while (isspace(*p)) | |
849 | + p++; | |
850 | + if (*typestr == '?') { | |
851 | + typestr++; | |
852 | + if (*p == '\0') { | |
853 | + /* no optional string: NULL argument */ | |
854 | + str = NULL; | |
855 | + goto add_str; | |
856 | + } | |
857 | + } | |
858 | + ret = get_str(buf, sizeof(buf), &p); | |
859 | + if (ret < 0) { | |
860 | + if (c == 'F') | |
861 | + term_printf("%s: filename expected\n", cmdname); | |
862 | + else | |
863 | + term_printf("%s: string expected\n", cmdname); | |
864 | + goto fail; | |
865 | + } | |
866 | + str = qemu_malloc(strlen(buf) + 1); | |
867 | + strcpy(str, buf); | |
868 | + str_allocated[nb_args] = str; | |
869 | + add_str: | |
870 | + if (nb_args >= MAX_ARGS) { | |
871 | + error_args: | |
872 | + term_printf("%s: too many arguments\n", cmdname); | |
873 | + goto fail; | |
874 | + } | |
875 | + args[nb_args++] = str; | |
876 | + } | |
373 | 877 | break; |
374 | - if (*p == '\0') | |
878 | + case '/': | |
879 | + { | |
880 | + int count, format, size; | |
881 | + | |
882 | + while (isspace(*p)) | |
883 | + p++; | |
884 | + if (*p == '/') { | |
885 | + /* format found */ | |
886 | + p++; | |
887 | + count = 1; | |
888 | + if (isdigit(*p)) { | |
889 | + count = 0; | |
890 | + while (isdigit(*p)) { | |
891 | + count = count * 10 + (*p - '0'); | |
892 | + p++; | |
893 | + } | |
894 | + } | |
895 | + size = -1; | |
896 | + format = -1; | |
897 | + for(;;) { | |
898 | + switch(*p) { | |
899 | + case 'o': | |
900 | + case 'd': | |
901 | + case 'u': | |
902 | + case 'x': | |
903 | + case 'i': | |
904 | + case 'c': | |
905 | + format = *p++; | |
906 | + break; | |
907 | + case 'b': | |
908 | + size = 1; | |
909 | + p++; | |
910 | + break; | |
911 | + case 'h': | |
912 | + size = 2; | |
913 | + p++; | |
914 | + break; | |
915 | + case 'w': | |
916 | + size = 4; | |
917 | + p++; | |
918 | + break; | |
919 | + case 'g': | |
920 | + case 'L': | |
921 | + size = 8; | |
922 | + p++; | |
923 | + break; | |
924 | + default: | |
925 | + goto next; | |
926 | + } | |
927 | + } | |
928 | + next: | |
929 | + if (*p != '\0' && !isspace(*p)) { | |
930 | + term_printf("invalid char in format: '%c'\n", *p); | |
931 | + goto fail; | |
932 | + } | |
933 | + if (size < 0) | |
934 | + size = default_fmt_size; | |
935 | + if (format < 0) | |
936 | + format = default_fmt_format; | |
937 | + default_fmt_size = size; | |
938 | + default_fmt_format = format; | |
939 | + } else { | |
940 | + count = 1; | |
941 | + format = default_fmt_format; | |
942 | + size = default_fmt_size; | |
943 | + } | |
944 | + if (nb_args + 3 > MAX_ARGS) | |
945 | + goto error_args; | |
946 | + args[nb_args++] = (void*)count; | |
947 | + args[nb_args++] = (void*)format; | |
948 | + args[nb_args++] = (void*)size; | |
949 | + } | |
950 | + break; | |
951 | + case 'i': | |
952 | + { | |
953 | + int val; | |
954 | + while (isspace(*p)) | |
955 | + p++; | |
956 | + if (*typestr == '?') { | |
957 | + typestr++; | |
958 | + if (*p == '\0') | |
959 | + has_arg = 0; | |
960 | + else | |
961 | + has_arg = 1; | |
962 | + if (nb_args >= MAX_ARGS) | |
963 | + goto error_args; | |
964 | + args[nb_args++] = (void *)has_arg; | |
965 | + if (!has_arg) { | |
966 | + if (nb_args >= MAX_ARGS) | |
967 | + goto error_args; | |
968 | + val = -1; | |
969 | + goto add_num; | |
970 | + } | |
971 | + } | |
972 | + if (get_expr(&val, &p)) | |
973 | + goto fail; | |
974 | + add_num: | |
975 | + if (nb_args >= MAX_ARGS) | |
976 | + goto error_args; | |
977 | + args[nb_args++] = (void *)val; | |
978 | + } | |
375 | 979 | break; |
376 | - *p++ = '\0'; | |
980 | + case '-': | |
981 | + { | |
982 | + int has_option; | |
983 | + /* option */ | |
984 | + | |
985 | + c = *typestr++; | |
986 | + if (c == '\0') | |
987 | + goto bad_type; | |
988 | + while (isspace(*p)) | |
989 | + p++; | |
990 | + has_option = 0; | |
991 | + if (*p == '-') { | |
992 | + p++; | |
993 | + if (*p != c) { | |
994 | + term_printf("%s: unsupported option -%c\n", | |
995 | + cmdname, *p); | |
996 | + goto fail; | |
997 | + } | |
998 | + p++; | |
999 | + has_option = 1; | |
1000 | + } | |
1001 | + if (nb_args >= MAX_ARGS) | |
1002 | + goto error_args; | |
1003 | + args[nb_args++] = (void *)has_option; | |
1004 | + } | |
1005 | + break; | |
1006 | + default: | |
1007 | + bad_type: | |
1008 | + term_printf("%s: unknown type '%c'\n", cmdname, c); | |
1009 | + goto fail; | |
1010 | + } | |
377 | 1011 | } |
378 | - args[argc] = NULL; | |
379 | -#ifdef DEBUG | |
380 | - for(i=0;i<argc;i++) { | |
381 | - term_printf(" '%s'", args[i]); | |
1012 | + /* check that all arguments were parsed */ | |
1013 | + while (isspace(*p)) | |
1014 | + p++; | |
1015 | + if (*p != '\0') { | |
1016 | + term_printf("%s: extraneous characters at the end of line\n", | |
1017 | + cmdname); | |
1018 | + goto fail; | |
382 | 1019 | } |
383 | - term_printf("\n"); | |
384 | -#endif | |
385 | - if (argc <= 0) | |
386 | - return; | |
387 | - for(cmd = term_cmds; cmd->name != NULL; cmd++) { | |
388 | - if (compare_cmd(args[0], cmd->name)) | |
389 | - goto found; | |
1020 | + | |
1021 | + switch(nb_args) { | |
1022 | + case 0: | |
1023 | + cmd->handler(); | |
1024 | + break; | |
1025 | + case 1: | |
1026 | + cmd->handler(args[0]); | |
1027 | + break; | |
1028 | + case 2: | |
1029 | + cmd->handler(args[0], args[1]); | |
1030 | + break; | |
1031 | + case 3: | |
1032 | + cmd->handler(args[0], args[1], args[2]); | |
1033 | + break; | |
1034 | + case 4: | |
1035 | + cmd->handler(args[0], args[1], args[2], args[3]); | |
1036 | + break; | |
1037 | + case 5: | |
1038 | + cmd->handler(args[0], args[1], args[2], args[3], args[4]); | |
1039 | + break; | |
1040 | + default: | |
1041 | + term_printf("unsupported number of arguments: %d\n", nb_args); | |
1042 | + goto fail; | |
390 | 1043 | } |
391 | - term_printf("unknown command: '%s'\n", args[0]); | |
1044 | + fail: | |
1045 | + for(i = 0; i < MAX_ARGS; i++) | |
1046 | + qemu_free(str_allocated[i]); | |
392 | 1047 | return; |
393 | - found: | |
394 | - cmd->handler(argc, args); | |
395 | 1048 | } |
396 | 1049 | |
397 | 1050 | static void term_show_prompt(void) | ... | ... |