// Copyright (C) 2001 MandrakeSoft S.A. // // MandrakeSoft S.A. // 43, rue d'Aboukir // 75002 Paris - France // http://www.linux-mandrake.com/ // http://www.mandrakesoft.com/ // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // define shortcuts to get register from the default CPU #define EBP (BX_CPU(dbg_cpu)->gen_reg[5].erx) #define EIP (BX_CPU(dbg_cpu)->eip) #define ESP (BX_CPU(dbg_cpu)->gen_reg[4].erx) #define SP (BX_CPU(dbg_cpu)->gen_reg[4].word.rx) extern "C" { #include } #include "bochs.h" #define LOG_THIS genlog-> #if HAVE_LIBREADLINE extern "C" { #include #include #if HAVE_READLINE_HISTORY_H #include #endif } #endif static unsigned doit = 0; #define SIM_NAME0 "bochs" #ifndef SIM_NAME1_STR #define SIM_NAME1_STR "sim1" #endif #define SIM_NAME(x) ((x == 0) ? SIM_NAME0 : SIM_NAME1_STR) // default CPU in the debugger. For commands like "dump_cpu" it will // use the default instead of always dumping all cpus. Bit32u dbg_cpu = 0; static void bx_dbg_usage(void); static char bx_debug_rc_fname[BX_MAX_PATH]; static char tmp_buf[512]; static char *tmp_buf_ptr; static char *argv0 = NULL; #if BX_NUM_SIMULATORS >= 2 #define BX_DBG_IO_JOURNAL_SIZE 1024 #define BX_DBG_UCMEM_JOURNAL_SIZE 1024 #define BX_DBG_ASYNC_JOURNAL_SIZE 1024 #define BX_DBG_MASTER_MODE 10 #define BX_DBG_SLAVE_MODE 11 // #define BX_DBG_DEFAULT_ICOUNT_QUANTUM 50 #define BX_DBG_DEFAULT_ICOUNT_QUANTUM 3 /* mch */ static unsigned bx_dbg_cosimulateN(bx_dbg_icount_t count); static int bx_dbg_compare_sim_iaddr(void); static Boolean bx_dbg_compare_sim_cpu(void); static Boolean bx_dbg_compare_sim_memory(void); static void bx_dbg_journal_a20_event(unsigned val); #endif static struct { #if BX_NUM_SIMULATORS >= 2 // some fields used only for cosimulation unsigned icount_quantum; unsigned master_slave_mode; unsigned master, slave; struct { struct { Bit8u op; Bit8u len; Bit16u addr; Bit32u value; } element[BX_DBG_IO_JOURNAL_SIZE]; unsigned size; unsigned head, tail; } IO_journal; struct { struct { Bit8u op; Bit8u len; Bit32u addr; Bit32u value; } element[BX_DBG_UCMEM_JOURNAL_SIZE]; unsigned size; unsigned head, tail; } UCmem_journal; // need to handle DMA stuff in here... #define BX_DBG_ASYNC_JOURNAL_NONE 0 #define BX_DBG_ASYNC_JOURNAL_A20 1 #define BX_DBG_ASYNC_JOURNAL_IAC 2 #define BX_DBG_ASYNC_JOURNAL_NMI 3 #define BX_DBG_ASYNC_JOURNAL_RESET 4 // Asynchronous events at the boundaries they are *taken* by the master simulator. // These are replayed back to the slave at the same boundaries. struct { struct { unsigned what; // A20, INTR, NMI, RESET, IAC, ... bx_dbg_icount_t icount; union { struct { unsigned val; } a20, nmi, reset, iac; // perhaps other more complex types here } u; } element[BX_DBG_ASYNC_JOURNAL_SIZE]; unsigned size; unsigned head, tail; } async_journal; struct { Boolean iaddr; Boolean cpu; Boolean memory; } compare_at_sync; Boolean fast_forward_mode; #endif // #if BX_NUM_SIMULATORS >= 2 // some fields used for single CPU debugger Boolean auto_disassemble; unsigned disassemble_size; char default_display_format; char default_unit_size; Bit32u default_addr; unsigned next_bpoint_id; // last icount known to be in sync #if BX_DBG_ICOUNT_SIZE == 32 Bit32u last_sync_icount; #else // BX_DBG_ICOUNT_SIZE == 64 Bit64u last_sync_icount; #endif } bx_debugger; // cosim commands for handling of comparison of simulator // environments when both simulators have reached a common // point (synchronized). // cosim compare_at_sync iaddr (default is on) // cosim compare_at_sync cpu (default is off) // cosim compare_at_sync memory (default is off) // cosim compare iaddr // cosim compare cpu // cosim compare memory typedef struct { FILE *fp; char fname[BX_MAX_PATH]; unsigned lineno; } bx_infile_stack_entry_t; bx_infile_stack_entry_t bx_infile_stack[BX_INFILE_DEPTH]; int bx_infile_stack_index = 0; static int bx_nest_infile(char *path); static void bx_debug_ctrlc_handler(int signum); static void bx_unnest_infile(void); static void bx_get_command(void); static void bx_dbg_print_guard_results(); static void bx_dbg_breakpoint_changed(void); bx_dbg_callback_t bx_dbg_callback[BX_NUM_SIMULATORS]; bx_guard_t bx_guard; // DMA stuff void bx_dbg_post_dma_reports(void); #define BX_BATCH_DMA_BUFSIZE 512 static struct { unsigned this_many; // batch this many max before posting events unsigned Qsize; // this many have been batched struct { Bit32u addr; // address of DMA op unsigned len; // number of bytes in op unsigned what; // BX_READ or BX_WRITE Bit32u val; // value of DMA op bx_dbg_icount_t icount; // icount at this dma op } Q[BX_BATCH_DMA_BUFSIZE]; } bx_dbg_batch_dma; // some buffers for disassembly #if BX_DISASM static Bit8u bx_disasm_ibuf[32]; static char bx_disasm_tbuf[512]; #endif int bx_dbg_main(int argc, char *argv[]) { int i, bochs_argc=0, sim1_argc=0, sim2_argc=0; char **bochs_argv = NULL; char **sim1_argv = NULL; char **sim2_argv = NULL; argc = 1; bx_dbg_batch_dma.this_many = 1; bx_dbg_batch_dma.Qsize = 0; // initialize callback functions, and guard environment memset(bx_dbg_callback, 0, sizeof(bx_dbg_callback)); memset(&bx_guard, 0, sizeof(bx_guard)); bx_guard.async.irq = 1; bx_guard.async.dma = 1; memset(&bx_debugger, 0, sizeof(bx_debugger)); #if BX_NUM_SIMULATORS >= 2 bx_debugger.icount_quantum = BX_DBG_DEFAULT_ICOUNT_QUANTUM; bx_debugger.IO_journal.size = 0; bx_debugger.IO_journal.head = 0; bx_debugger.IO_journal.tail = 0; bx_debugger.UCmem_journal.size = 0; bx_debugger.UCmem_journal.head = 0; bx_debugger.UCmem_journal.tail = 0; bx_debugger.async_journal.size = 0; bx_debugger.async_journal.head = 0; bx_debugger.async_journal.tail = 0; bx_debugger.master = 0; bx_debugger.slave = 1; bx_debugger.compare_at_sync.iaddr = 1; bx_debugger.fast_forward_mode = 0; #endif bx_debugger.auto_disassemble = 1; bx_debugger.disassemble_size = 32; bx_debugger.default_display_format = 'x'; bx_debugger.default_unit_size = 'w'; bx_debugger.default_addr = 0; bx_debugger.next_bpoint_id = 1; bx_debugger.last_sync_icount = 0; argv0 = strdup(argv[0]); bx_debug_rc_fname[0] = '\0'; bochs_argv = (char **) &argv[0]; sim1_argv = bochs_argv; // start out with something reasonable sim2_argv = bochs_argv; // start out with something reasonable bochs_argc = 1; sim1_argc = 1; sim2_argc = 1; // process "-rc pathname" option, if it exists i = 1; if ( (argc >= 2) && !strcmp(argv[1], "-rc") ) { if ( argc == 2 ) { BX_ERROR(( "%s: -rc option used, but no path specified.", argv[0] )); bx_dbg_usage(); exit(1); } strncpy(bx_debug_rc_fname, argv[2], BX_MAX_PATH-1); i += 2; // skip past "-rc" and filename bochs_argv = (char **) &argv[2]; } // process options to bochs framework for (; i 1 BX_SIM2_INIT(&bx_dbg_callback[1], sim2_argc, sim2_argv); #endif // parse any remaining args in the usual way bx_parse_cmdline (bochs_argc, bochs_argv); // initialize hardware bx_init_hardware(); // doesn't this duplicate things? #if BX_NUM_SIMULATORS >= 2 bx_debugger.compare_at_sync.cpu = 0; bx_debugger.compare_at_sync.memory = 0; #endif // call init routines for each CPU+mem simulator // initialize for SMP. one memory, multiple processors. #if BX_SUPPORT_APIC memset(apic_index, 0, sizeof(apic_index[0]) * APIC_MAX_ID); #endif #if BX_SMP_PROCESSORS==1 BX_MEM(0)->init_memory(bx_options.memory.Osize->get () * 1024*1024); BX_MEM(0)->load_ROM(bx_options.rom.Opath->getptr (), bx_options.rom.Oaddress->get ()); BX_MEM(0)->load_ROM(bx_options.vgarom.Opath->getptr (), 0xc0000); BX_CPU(0)->init (BX_MEM(0)); BX_CPU(0)->reset(BX_RESET_HARDWARE); #else // SMP initialization bx_mem_array[0] = new BX_MEM_C (); bx_mem_array[0]->init_memory(bx_options.memory.Osize->get () * 1024*1024); bx_mem_array[0]->load_ROM(bx_options.rom.Opath->getptr (), bx_options.rom.Oaddress->get ()); bx_mem_array[0]->load_ROM(bx_options.vgarom.Opath->getptr (), 0xc0000); for (int i=0; iinit (BX_MEM(0)); // assign apic ID from the index of this loop // if !BX_SUPPORT_APIC, this will not compile. BX_CPU(i)->local_apic.set_id (i); BX_CPU(i)->reset(BX_RESET_HARDWARE); } #endif #if BX_NUM_SIMULATORS > 1 #error cosimulation not supported until SMP stuff settles BX_MEM(1) = new BX_MEM_C (); BX_CPU(1) = new BX_CPU_C (BX_MEM(1)); BX_CPU(1)->reset(BX_RESET_HARDWARE); BX_MEM(1)->init_memory(bx_options.memory.Osize->get () * 1024*1024); BX_MEM(1)->load_ROM(bx_options.rom.path->getptr (), bx_options.rom.address->get ()); BX_MEM(1)->load_ROM(bx_options.vgarom.path->getptr (), 0xc0000); #endif // (mch) Moved from main.cc bx_devices.init(BX_MEM(0)); bx_gui.init_signal_handlers (); bx_pc_system.start_timers(); // setup Ctrl-C handler signal(SIGINT, bx_debug_ctrlc_handler); bx_dbg_user_input_loop(); bx_dbg_exit(0); return(0); // keep compiler happy } void bx_dbg_usage(void) { fprintf (stderr, "usage: %s [-rc path] [-sim1 ... ] [-sim2 ... ]\n", argv0); } void bx_dbg_user_input_loop(void) { int reti; unsigned include_cmd_len = strlen(BX_INCLUDE_CMD); while ( 1 ) { bx_get_command(); if ( (*tmp_buf_ptr == '\n') || (*tmp_buf_ptr == 0) ) { if (bx_infile_stack_index == 0) fprintf(stderr, "\n"); } else if ( (strncmp(tmp_buf_ptr, BX_INCLUDE_CMD, include_cmd_len) == 0) && (tmp_buf_ptr[include_cmd_len] == ' ' || tmp_buf_ptr[include_cmd_len] == '\t') ) { char *ptr; int len; ptr = tmp_buf_ptr + include_cmd_len+1; while ( *ptr==' ' || *ptr=='\t' ) ptr++; len = strlen(ptr); if (len == 0) { fprintf(stderr, "%s: no filename given to 'source' command.\n", argv0); if (bx_infile_stack_index > 0) { fprintf(stderr, "%s: ERROR in source file causes exit.\n", argv0); bx_dbg_exit(1); } continue; } ptr[len-1] = 0; // get rid of newline reti = bx_nest_infile(ptr); if ((reti==0) && (bx_infile_stack_index > 0)) { fprintf(stderr, "%s: ERROR in source file causes exit.\n", argv0); bx_dbg_exit(1); } } else { // Give a chance to the command line extensions, to // consume the command. If they return 0, then // we need to process the command. A return of 1 // means, the extensions have handled the command if ( bx_dbg_extensions(tmp_buf_ptr)==0 ) { // process command here bx_add_lex_input(tmp_buf_ptr); bxparse(); } } } } void bx_get_command(void) { char *charptr_ret; bx_infile_stack[bx_infile_stack_index].lineno++; char prompt[256]; if (bx_infile_stack_index == 0) { sprintf(prompt, " ", bx_infile_stack[bx_infile_stack_index].lineno); } #if HAVE_LIBREADLINE if (bx_infile_stack_index == 0) { charptr_ret = readline (prompt); // beware, returns NULL on end of file if (charptr_ret && strlen(charptr_ret) > 0) { add_history (charptr_ret); strcpy (tmp_buf, charptr_ret); strcat (tmp_buf, "\n"); free (charptr_ret); charptr_ret = &tmp_buf[0]; } } else { charptr_ret = fgets(tmp_buf, 512, bx_infile_stack[bx_infile_stack_index].fp); } #else if (bx_infile_stack_index == 0) fprintf (stderr, "%s", prompt); charptr_ret = fgets(tmp_buf, 512, bx_infile_stack[bx_infile_stack_index].fp); #endif if (charptr_ret == NULL) { // see if error was due to EOF condition if (feof(bx_infile_stack[bx_infile_stack_index].fp)) { if (bx_infile_stack_index > 0) { // nested level of include files, pop back to previous one bx_unnest_infile(); } else { // not nested, sitting at stdin prompt, user wants out bx_dbg_quit_command(); } // call recursively bx_get_command(); return; } // error was not EOF, see if it was from a Ctrl-C if (bx_guard.interrupt_requested) { tmp_buf[0] = '\n'; tmp_buf[1] = 0; tmp_buf_ptr = &tmp_buf[0]; bx_guard.interrupt_requested = 0; return; } fprintf(stderr, "fgets() returned ERROR.\n"); fprintf(stderr, "intr request was %u\n", bx_guard.interrupt_requested); bx_dbg_exit(1); } tmp_buf_ptr = &tmp_buf[0]; // look for first non-whitespace character while ( ((*tmp_buf_ptr == ' ') || (*tmp_buf_ptr == '\t')) && (*tmp_buf_ptr != '\n') && (*tmp_buf_ptr != 0) ) { tmp_buf_ptr++; } return; } int bx_nest_infile(char *path) { FILE *tmp_fp; tmp_fp = fopen(path, "r"); if (!tmp_fp) { fprintf(stderr, "%s: can not open file '%s' for reading.\n", argv0, path); return(0); } if ( (bx_infile_stack_index+1) >= BX_INFILE_DEPTH ) { fprintf(stderr, "%s: source files nested too deeply\n", argv0); return(0); } bx_infile_stack_index++; bx_infile_stack[bx_infile_stack_index].fp = tmp_fp; strncpy(bx_infile_stack[bx_infile_stack_index].fname, path, BX_MAX_PATH); bx_infile_stack[bx_infile_stack_index].fname[BX_MAX_PATH-1] = 0; bx_infile_stack[bx_infile_stack_index].lineno = 0; return(1); } void bx_unnest_infile(void) { if (bx_infile_stack_index <= 0) { fprintf(stderr, "%s: ERROR: unnest_infile(): nesting level = 0.\n", argv0); bx_dbg_exit(1); } fclose(bx_infile_stack[bx_infile_stack_index].fp); bx_infile_stack_index--; } int bxwrap(void) { fprintf(stderr, "%s: ERROR: bxwrap() called.\n", argv0); bx_dbg_exit(1); return(0); // keep compiler quiet } void bxerror(char *s) { fprintf(stderr, "%s:%d: %s at '%s'\n", bx_infile_stack[bx_infile_stack_index].fname, bx_infile_stack[bx_infile_stack_index].lineno, s, bxtext); if (bx_infile_stack_index > 0) { fprintf(stderr, "%s: ERROR in source file causes exit.\n", argv0); bx_dbg_exit(1); } } void bx_debug_ctrlc_handler(int signum) { UNUSED(signum); BX_INFO(("Ctrl-C detected in signal handler.")); signal(SIGINT, bx_debug_ctrlc_handler); bx_guard.interrupt_requested = 1; } void bx_dbg_exit(int code) { BX_DEBUG(( "dbg: before sim1_exit" )); for (int cpu=0; cpu < BX_SMP_PROCESSORS; cpu++) { if (BX_CPU(cpu)) BX_CPU(cpu)->atexit(); } #if BX_NUM_SIMULATORS >= 2 fprintf(stderr, "before sim2_exit\n"); if (BX_CPU(1)) BX_CPU(1)->atexit(); #endif bx_atexit(); exit(code); } // // comands invoked from parser // void bx_dbg_quit_command(void) { BX_INFO(("dbg: Quit")); bx_dbg_exit(0); } void bx_dbg_trace_on_command(void) { BX_CPU(dbg_cpu)->trace = 1; fprintf (stderr, "Tracing enabled for %s\n", BX_CPU(dbg_cpu)->name); } void bx_dbg_trace_off_command(void) { BX_CPU(dbg_cpu)->trace = 0; fprintf (stderr, "Tracing disabled for %s\n", BX_CPU(dbg_cpu)->name); } void bx_dbg_ptime_command(void) { fprintf(stderr, "ptime: %lld\n", bx_pc_system.time_ticks()); #if BX_NUM_SIMULATORS >= 2 fprintf(stderr, #if BX_DBG_ICOUNT_SIZE == 32 "Last synchronized icount was %lu\n", (unsigned long) bx_debugger.last_sync_icount #else // BX_DBG_ICOUNT_SIZE == 64 "Last synchronized icount was %Lu\n", (unsigned long long) bx_debugger.last_sync_icount #endif /* BX_DBG_ICOUNT_SIZE == 32 */ ); #endif /* BX_NUM_SIMULATORS >= 2 */ } int timebp_timer = -1; Bit64u timebp_queue[MAX_CONCURRENT_BPS]; int timebp_queue_size = 0; void bx_dbg_timebp_command(Boolean absolute, Bit64u time) { long long diff = (absolute) ? time - bx_pc_system.time_ticks() : time; long long abs_time = (absolute) ? time : time + bx_pc_system.time_ticks(); if (diff < 0) { fprintf(stderr, "Request for time break point in the past. I can't let you do that.\n"); return; } if (timebp_queue_size == MAX_CONCURRENT_BPS) { fprintf(stderr, "Too many time break points\n"); return; } if (timebp_timer >= 0) { if (timebp_queue_size == 0 || abs_time < timebp_queue[0]) { /* first in queue */ for (int i = timebp_queue_size; i >= 0; i--) timebp_queue[i+1] = timebp_queue[i]; timebp_queue[0] = abs_time; timebp_queue_size++; bx_pc_system.activate_timer_ticks(timebp_timer, diff, 1); } else { /* not first, insert at suitable place */ for (int i = 1; i < timebp_queue_size; i++) { if (timebp_queue[i] == abs_time) { fprintf(stderr, "Time breakpoint not inserted (duplicate)\n"); return; } else if (abs_time < timebp_queue[i]) { for (int j = timebp_queue_size; j >= i; j++) timebp_queue[j+1] = timebp_queue[j]; timebp_queue[i] = abs_time; goto inserted; } } /* last */ timebp_queue[timebp_queue_size] = abs_time; inserted: timebp_queue_size++; } } else { timebp_queue_size = 1; timebp_queue[0] = abs_time; timebp_timer = bx_pc_system.register_timer_ticks(&bx_pc_system, bx_pc_system_c::timebp_handler, diff, 0, 1); } fprintf(stderr, "Time breakpoint inserted. Delta = %d\n", diff); } void bx_dbg_diff_memory(void) { #if BX_NUM_SIMULATORS < 2 printf("diff-memory supported only in cosimulation mode\n"); #else int num_pages = bx_options.memory.Osize->get () * 1024 / 4; for (int i = 0; i < num_pages; i++) { BX_CPU(dbg_cpu)->dbg_dirty_pages[i] = 1; } if (bx_dbg_compare_sim_memory()) printf("[diff-memory] Diff detected\n"); else printf("[diff-memory] No diff detected\n"); #endif /* NUM_SIMULATORS < 2 */ } void bx_dbg_sync_memory(Boolean set) { #if BX_NUM_SIMULATORS < 2 printf("sync-memory supported only in cosimulation mode\n"); #else bx_debugger.compare_at_sync.memory = set; printf("Memory sync %s\n", (set) ? "enabled" : "disabled"); #endif } void bx_dbg_sync_cpu(Boolean set) { #if BX_NUM_SIMULATORS < 2 printf("sync-cpu supported only in cosimulation mode\n"); #else bx_debugger.compare_at_sync.cpu = set; printf("Register file sync %s\n", (set) ? "enabled" : "disabled"); #endif } void bx_dbg_fast_forward(Bit32u num) { #if BX_NUM_SIMULATORS < 2 printf("fast-forward supported only in cosimulation mode\n"); #else printf("Entering fast-forward mode\n"); // Bit32u save_icount_quantum = bx_debugger.icount_quantum; // bx_debugger.icount_quantum = num; bx_guard.interrupt_requested = 0; bx_debugger.fast_forward_mode = 1; for (Bit32u e = 0; e < num; e += bx_debugger.icount_quantum) if (!bx_dbg_cosimulateN(bx_debugger.icount_quantum)) break; bx_debugger.fast_forward_mode = 0; // bx_debugger.icount_quantum = save_icount_quantum; bx_vga.timer_handler(&bx_vga); printf("Copying CPU...\n"); bx_dbg_cpu_t cpu; if (!BX_CPU(0)->dbg_get_cpu(&cpu) || !BX_CPU(1)->dbg_set_cpu(&cpu)) printf("Error copying CPU data!\n"); printf("Copying memory...\n"); int num_pages = bx_options.memory.Osize->get () * 1024 / 4; for (int i = 0; i < num_pages; i++) { if (BX_CPU(0)->dbg_dirty_pages[i]) { Bit32u page_start = i * 1024 * 4; printf("Copying page %08x\n", page_start); extern Bit8u* SIM1_GET_PHYS_PTR(Bit32u page_start); Bit8u* sim0_page_vec = bx_mem0.vector + page_start; Bit8u* sim1_page_vec = SIM1_GET_PHYS_PTR(page_start); memcpy(sim1_page_vec, sim0_page_vec, 1024 * 4); } } printf("Taking async events...\n"); printf("Exiting fast-forward mode\n"); #endif } static Bit32u conv_4xBit8u_to_Bit32u (Bit8u* buf) { Bit32u ret = 0; for (int i = 0; i < 4; i++) { ret |= (buf[i] << (8 * i)); } return ret; } /* (mch) Print various info for logical address. */ void bx_dbg_info_address(Bit32u seg_reg_num, Bit32u offset) { #if BX_NUM_SIMULATORS < 2 printf("addr-info only supported in cosim configuration.\n"); #else for (int sim = 0; sim < 2; sim++) { /* Find page table base address */ bx_dbg_cpu_t regs; BX_CPU(sim)->dbg_get_cpu(®s); Bit32u base = regs.cr3 & ~0xfff; Bit8u buf[4]; Bit32u directory_addr = base + (offset >> 22) * 4; Bit32u directory; if (BX_CPU(sim)->mem->dbg_fetch_mem(directory_addr, 4, buf)) { directory = conv_4xBit8u_to_Bit32u(buf); Bit32u table_addr = (directory & ~0xfff) + ((offset >> 12) & 0x3ff) * 4; Bit32u table; printf("[%s] ", SIM_NAME(sim)); printf("PDE: %08x (", directory); printf("%s, %s, %s, %s, %s)", (directory & 1) ? "Present" : "Not present", (directory & 2) ? "Read/Write" : "Read-only", (directory & 4) ? "User" : "Supervisor", (directory & (1 << 5)) ? "Accessed" : "-", (directory & (1 << 6)) ? "Dirty" : "-"); if (directory & 1) { if (BX_CPU(sim)->mem->dbg_fetch_mem(table_addr, 4, buf)) { table = conv_4xBit8u_to_Bit32u(buf); printf(", PTE: %08x (", table); printf("%s, %s, %s, %s, %s)\n", (table & 1) ? "Present" : "Not present", (table & 2) ? "Read/Write" : "Read-only", (table & 4) ? "User" : "Supervisor", (table & (1 << 5)) ? "Accessed" : "-", (table & (1 << 6)) ? "Dirty" : "-"); } else { printf("[%s] Could not read from physical address %08x\n", SIM_NAME(sim), directory_addr); return; } } else { printf("\n"); } } else { printf("[%s] Could not read from physical address %08x\n", SIM_NAME(sim), directory_addr); return; } } #endif } void bx_dbg_record_command(char* path_quoted) { // skip beginning double quote if (path_quoted[0] == '"') path_quoted++; // null out ending quote int len = strlen(path_quoted); if (path_quoted[len - 1] == '"') path_quoted[len - 1] = '\0'; bx_dbg.record_io = fopen(path_quoted, "w"); if (bx_dbg.record_io) fprintf(stderr, "IO record file '%s' opened\n", path_quoted); else fprintf(stderr, "Error opening '%s' for writing\n", path_quoted); } static FILE* playback_file = 0; struct playback_entry_t { char command[100]; Bit32u argument; void trigger (); }; static playback_entry_t playback_entry; static long long last_playback_time = 0; static int playback_timer_index = -1; void bx_dbg_playback_command(char* path_quoted) { // skip beginning double quote if (path_quoted[0] == '"') path_quoted++; // null out ending quote int len = strlen(path_quoted); if (path_quoted[len - 1] == '"') path_quoted[len - 1] = '\0'; playback_file = fopen(path_quoted, "r"); if (playback_file) { fprintf(stderr, "Playback from '%s'\n", path_quoted); last_playback_time = 0; fprintf(stderr, "playback times relative from %lld\n", bx_pc_system.time_ticks()); enter_playback_entry(); } else fprintf(stderr, "Error opening '%s' for reading\n", path_quoted); } // BW added. toggles vm86 mode switch breakpoint //dummy not used and may be null void bx_dbg_modebp_command(char* dummy) { BX_CPU(dbg_cpu)->debug_vm == BX_CPU(dbg_cpu)->eflags.vm; BX_CPU(dbg_cpu)->mode_break = !BX_CPU(dbg_cpu)->mode_break; fprintf(stderr," mode switch break %s\n", BX_CPU(dbg_cpu)->mode_break ? "enabled" : "disabled"); } // where // stack trace: ebp -> old ebp // return eip at ebp + 4 void bx_dbg_where_command() { if (!BX_CPU(dbg_cpu)->protected_mode()) { fprintf(stderr, "'where' only supported in protected mode\n"); return; } if (BX_CPU(dbg_cpu)->sregs[BX_SREG_SS].cache.u.segment.base != 0) { fprintf(stderr, "non-zero stack base\n"); return; } Bit32u bp = EBP; Bit32u ip = EIP; fprintf(stderr, "(%d) 0x%08x\n", 0, ip); for (int i = 1; i < 50; i++) { // Up Boolean paddr_valid; Bit32u paddr; Bit8u buf[4]; // bp = [bp]; BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(bp, &paddr, &paddr_valid); if (paddr_valid) { if (BX_MEM(0)->dbg_fetch_mem(paddr, 4, buf)) { bp = conv_4xBit8u_to_Bit32u(buf); } else { fprintf(stderr, "(%d) Physical memory read error (BP)\n", i); break; } } else { fprintf(stderr, "(%d) Could not translate linear address (BP)\n", i); break; } // ip = [bp + 4]; BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(bp + 4, &paddr, &paddr_valid); if (paddr_valid) { if (BX_MEM(0)->dbg_fetch_mem(paddr, 4, buf)) { ip = conv_4xBit8u_to_Bit32u(buf); } else { fprintf(stderr, "(%d) Physical memory read error (IP)\n", i); break; } } else { fprintf(stderr, "(%d) Could not translate linear address (IP)\n", i); break; } // Print fprintf(stderr, "(%d) 0x%08x\n", i, ip); } } void bx_dbg_print_string_command(Bit32u start_addr) { fprintf(stderr, "0x%08x: ", start_addr); for (int i = 0; ; i++) { Bit32u paddr; Bit32u paddr_valid; Bit8u buf[1]; BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(start_addr+i, &paddr, &paddr_valid); if (paddr_valid) { if (BX_MEM(0)->dbg_fetch_mem(paddr, 1, buf)) { if (buf[0] == 0) break; if (isgraph(buf[0]) || buf[0] == 0x20) fprintf(stderr, "%c", buf[0]); else fprintf(stderr, "\\%d", buf[0]); } else { fprintf(stderr, ""); break; } } else { fprintf(stderr, ""); break; } } fprintf(stderr, "\n"); } static Bit32u last_cr3; static int last_pe = 0; static int last_vm = 0; unsigned int dbg_show_mask = 0; // 0x80 print mode // 0x40 print interrupts // 0x20 print calls //BW added. toggles show symbolic info (calls to begin with) // 0x1 call // 0x2 return // 0x4 int // 0x8 iret // 0x10 interrupts (includes iret) static void dbg_dump_table(Boolean); void bx_dbg_show_command(char* arg) { if(arg) { if (!strcmp(arg,"\"mode\"")){ dbg_show_mask = 0x80; } else if (!strcmp(arg,"\"int\"")){ dbg_show_mask = 0xc0; } else if(!strcmp(arg,"\"call\"")){ dbg_show_mask = 0xe0; } else if(!strcmp(arg,"\"ret\"")){ dbg_show_mask = 0xe0; } else if(!strcmp(arg,"\"off\"")){ dbg_show_mask = 0x0; } else if(!strcmp(arg,"\"tab\"")){ dbg_dump_table(1); return; } else if(!strcmp(arg,"\"c\"")){ dbg_dump_table(0); return; } else if(!strcmp(arg,"\"dbg-all\"")){ bx_dbg.floppy = 1; bx_dbg.keyboard = 1; bx_dbg.video = 1; bx_dbg.disk = 1; bx_dbg.pit = 1; bx_dbg.pic = 1; bx_dbg.bios = 1; bx_dbg.cmos = 1; bx_dbg.a20 = 1; bx_dbg.interrupts = 1; bx_dbg.exceptions = 1; bx_dbg.unsupported = 1; bx_dbg.temp = 1; bx_dbg.reset = 1; bx_dbg.mouse = 1; bx_dbg.io = 1; bx_dbg.debugger = 1; bx_dbg.xms = 1; bx_dbg.v8086 = 1; bx_dbg.paging = 1; bx_dbg.creg = 1; bx_dbg.dreg = 1; bx_dbg.dma = 1; bx_dbg.unsupported_io = 1; /* bx_dbg.record_io = 1; this is a pointer .. somewhere */ printf("Turned on all bx_dbg flags\n"); return; } else if(!strcmp(arg,"\"none\"")){ bx_dbg.floppy = 0; bx_dbg.keyboard = 0; bx_dbg.video = 0; bx_dbg.disk = 0; bx_dbg.pit = 0; bx_dbg.pic = 0; bx_dbg.bios = 0; bx_dbg.cmos = 0; bx_dbg.a20 = 0; bx_dbg.interrupts = 0; bx_dbg.exceptions = 0; bx_dbg.unsupported = 0; bx_dbg.temp = 0; bx_dbg.reset = 0; bx_dbg.mouse = 0; bx_dbg.io = 0; bx_dbg.debugger = 0; bx_dbg.xms = 0; bx_dbg.v8086 = 0; bx_dbg.paging = 0; bx_dbg.creg = 0; bx_dbg.dreg = 0; bx_dbg.dma = 0; bx_dbg.unsupported_io = 0; /* bx_dbg.record_io = 0; this is a pointer .. somewhere */ printf("Turned off all bx_dbg flags\n"); return; } else if(!strcmp(arg,"\"vga\"")){ bx_vga.timer (); return; } else { printf("Unrecognized arg: %s ('mode' 'int' 'call' 'ret' 'dbg-all' are valid)\n",arg); return; } } else { fprintf(stderr," show mask is 0x%x\n", dbg_show_mask); return; } // enable trace if any print is active if(dbg_show_mask & 0xe0) dbg_show_mask |= 0x1f; fprintf(stderr," show mask is 0x%x, cleared show_flag\n", dbg_show_mask); BX_CPU(dbg_cpu)->show_flag = 0; last_cr3 = BX_CPU(dbg_cpu)->cr3; last_pe = BX_CPU(dbg_cpu)->cr0.pe; last_vm = BX_CPU(dbg_cpu)->eflags.vm; fprintf(stderr,"%10lld: address %04x:%08x %08x\n\n", bx_pc_system.time_ticks(), BX_CPU(dbg_cpu)->guard_found.cs, BX_CPU(dbg_cpu)->guard_found.eip, BX_CPU(dbg_cpu)->guard_found.laddr); } void playback_function(void* this_ptr) { ((playback_entry_t*)this_ptr)->trigger(); } void enter_playback_entry() { static const int playback_buf_size = 100; char playback_buf[playback_buf_size]; if (!fgets(playback_buf, playback_buf_size, playback_file)) return; Bit64u time; Bit32u argument; if (sscanf(playback_buf, "%s %lld %x", playback_entry.command, &time, &playback_entry.argument) != 3) { fprintf(stderr, "Parse error in playback string '%s'\n", playback_buf); return; } long long diff = time - last_playback_time; last_playback_time = time; if (diff < 0) { BX_PANIC(("Negative diff in playback")); } else if (diff == 0) { playback_entry.trigger(); } else { if (playback_timer_index >= 0) bx_pc_system.activate_timer_ticks(playback_timer_index, diff, 0); else playback_timer_index = bx_pc_system.register_timer_ticks(&playback_entry, playback_function, diff, 0, 1); } } void playback_entry_t::trigger () { if (!strcmp("gen_scancode", command)) { bx_devices.keyboard->gen_scancode(argument); } else { fprintf(stderr, "Unknown playback command '%s'\n", command); return; } enter_playback_entry(); } void bx_dbg_print_stack_command(int nwords) { // Get linear address for stack top Bit32u sp = (BX_CPU(dbg_cpu)->sregs[BX_SREG_SS].cache.u.segment.d_b) ? ESP : SP; Bit32u linear_sp = sp + BX_CPU(dbg_cpu)->sregs[BX_SREG_SS].cache.u.segment.base; Bit8u buf[8]; for (int i = 0; i < nwords; i++) { Bit32u paddr; Boolean paddr_valid; BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(sp, &paddr, &paddr_valid); if (paddr_valid) { if (BX_MEM(0)->dbg_fetch_mem(paddr, 2, buf)) fprintf(stderr, " %08x [%08x] %04x\n", linear_sp, paddr, (int)buf[0] | ((int)buf[1] << 8)); else fprintf(stderr, " %08x [%08x] \n", paddr, linear_sp); } else { fprintf(stderr, " %08x \n", linear_sp); } sp += 2; linear_sp += 2; } } #if !BX_HAVE_HASH_MAP static char *BX_HAVE_HASH_MAP_ERR = "context not implemented because BX_HAVE_HASH_MAP=0\n"; char* bx_dbg_symbolic_address(Bit32u context, Bit32u eip, Bit32u base) { static Boolean first = true; if (first) { fprintf (stderr, BX_HAVE_HASH_MAP_ERR); first = false; } return "unknown context"; } void bx_dbg_symbol_command(char* filename, Boolean global, Bit32u offset) { fprintf (stderr, BX_HAVE_HASH_MAP_ERR); } #else /* if BX_HAVE_HASH_MAP == 1 */ /* Haven't figured out how to port this code to OSF1 cxx compiler. Until a more portable solution is found, at least make it easy to disable the template code: just set BX_HAVE_HASH_MAP=0 in config.h */ #include #include struct symbol_entry_t { symbol_entry_t (Bit32u _start = 0, char* _name = 0) { start = _start; name = _name; } char* name; Bit32u start; }; struct lt_symbol_entry_t { bool operator()(const symbol_entry_t* s1, const symbol_entry_t* s2) const { return s1->start < s2->start; } }; struct context_t { context_t (Bit32u); static context_t* get_context(Bit32u); symbol_entry_t* get_symbol_entry(Bit32u); void add_symbol(symbol_entry_t*); private: static hash_map* map; set* syms; Bit32u id; }; hash_map* context_t::map = new hash_map; context_t::context_t (Bit32u _id) { id = _id; syms = new set; (*map)[id] = this; } context_t* context_t::get_context(Bit32u i) { return (*map)[i]; } symbol_entry_t* context_t::get_symbol_entry(Bit32u ip) { symbol_entry_t probe; probe.start = ip; set::iterator iter = syms->upper_bound(&probe); if (iter == syms->end()) return 0; else if (iter == syms->begin()) return 0; else { iter--; return *iter; } } void context_t::add_symbol(symbol_entry_t* sym) { syms->insert(sym); } char* bx_dbg_symbolic_address(Bit32u context, Bit32u eip, Bit32u base) { static char buf[80]; if (base != 0) { snprintf (buf, 80, "non-zero base"); return buf; } // Look up this context context_t* cntx = context_t::get_context(context); if (!cntx) { // Try global context cntx = context_t::get_context(0); if (!cntx) { snprintf (buf, 80, "unknown context"); return buf; } } symbol_entry_t* entr = cntx->get_symbol_entry(eip); if (!entr) { snprintf (buf, 80, "no symbol"); return buf; } snprintf (buf, 80, "%s+%x", entr->name, eip - entr->start); return buf; } void bx_dbg_symbol_command(char* filename, Boolean global, Bit32u offset) { if (filename[0] == '"') filename++; int len = strlen(filename); if (filename[len - 1] == '"') filename[len - 1] = '\0'; // Install symbols in correct context (page table) // The file format should be // address symbol (example '00002afe _StartLoseNT') context_t* cntx = (global) ? context_t::get_context(0) : context_t::get_context((BX_CPU(dbg_cpu)->cr3) >> 12); if (!cntx) cntx = (global) ? new context_t(0) : new context_t((BX_CPU(dbg_cpu)->cr3) >> 12); FILE* fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "Could not open symbol file '%s'\n", filename); return; } char buf[200]; while (fgets(buf, 200, fp)) { // Parse char* sym_name = buf; for (int i = 0; i < 200 && buf[i]; i++) { if (buf[i] == ' ') { buf[i] = '\0'; sym_name = buf + i + 1; break; } } if (sym_name == buf) { fprintf(stderr, "Syntax error '%s'\n", buf); break; } Bit32u addr = strtoul(buf, 0, 16); if (sym_name[strlen(sym_name)-1] == '\n') sym_name[strlen(sym_name)-1] = '\0'; symbol_entry_t* sym = new symbol_entry_t(addr + offset, strdup(sym_name)); cntx->add_symbol(sym); } } #endif int num_write_watchpoints = 0; int num_read_watchpoints = 0; Bit32u write_watchpoint[MAX_WRITE_WATCHPOINTS]; Bit32u read_watchpoint[MAX_READ_WATCHPOINTS]; Boolean watchpoint_continue = 0; void bx_dbg_watch(int read, Bit32u address) { if (read == -1) { // print watch point info for (int i = 0; i < num_read_watchpoints; i++) { Bit8u buf[2]; if (BX_MEM(0)->dbg_fetch_mem(read_watchpoint[i], 2, buf)) fprintf(stderr, "read %08x (%04x)\n", read_watchpoint[i], (int)buf[0] | ((int)buf[1] << 8)); else fprintf(stderr, "read %08x (read error)\n", read_watchpoint[i]); } for (int i = 0; i < num_write_watchpoints; i++) { Bit8u buf[2]; if (BX_MEM(0)->dbg_fetch_mem(write_watchpoint[i], 2, buf)) fprintf(stderr, "write %08x (%04x)\n", write_watchpoint[i], (int)buf[0] | ((int)buf[1] << 8)); else fprintf(stderr, "write %08x (read error)\n", write_watchpoint[i]); } } else { if (read) { if (num_read_watchpoints == MAX_READ_WATCHPOINTS) { fprintf(stderr, "Too many read watchpoints\n"); return; } read_watchpoint[num_read_watchpoints++] = address; fprintf(stderr, "Read watchpoint at %08x inserted\n", address); } else { if (num_write_watchpoints == MAX_WRITE_WATCHPOINTS) { fprintf(stderr, "Too many write watchpoints\n"); return; } write_watchpoint[num_write_watchpoints++] = address; fprintf(stderr, "Write watchpoint at %08x inserted\n", address); } } } void bx_dbg_unwatch(int read, Bit32u address) { if (read == -1) { // unwatch all num_read_watchpoints = num_write_watchpoints = 0; fprintf(stderr, "All watchpoints removed\n"); } else { if (read) { fprintf(stderr, "Watchpoint remove not implemented\n"); } else { fprintf(stderr, "Watchpoint remove not implemented\n"); } } } void bx_dbg_continue_command(void) { // continue executing, until a guard found one_more: #if BX_NUM_SIMULATORS >= 2 bx_guard.interrupt_requested = 0; while (1) { if ( !bx_dbg_cosimulateN(bx_debugger.icount_quantum) ) break; } #else bx_guard.icount = 0; // I must guard for ICOUNT or one CPU could run forever without giving // the others a chance. bx_guard.guard_for |= BX_DBG_GUARD_ICOUNT; bx_guard.guard_for |= BX_DBG_GUARD_CTRL_C; // stop on Ctrl-C bx_guard.interrupt_requested = 0; int stop = 0; int which = -1; while (!stop) { int quantum = 5; // arbitrary number of cycles to run in each for (int cpu=0; cpu < BX_SMP_PROCESSORS; cpu++) { BX_CPU(cpu)->guard_found.guard_found = 0; BX_CPU(cpu)->guard_found.icount = 0; bx_guard.icount = quantum; BX_CPU(cpu)->cpu_loop (-1); // set stop flag if a guard found other than icount or halted unsigned long found = BX_CPU(cpu)->guard_found.guard_found; stop_reason_t reason = (stop_reason_t) BX_CPU(cpu)->stop_reason; if (found & BX_DBG_GUARD_ICOUNT) { // I expected this guard, don't stop } else if (found!=0) { BX_INFO(("found guard other than icount in %s..stopping", BX_CPU(cpu)->name)); stop = 1; which = cpu; } else if (reason != STOP_NO_REASON && reason != STOP_CPU_HALTED) { stop = 1; which = cpu; } // even if stop==1, finish cycling through all processors. // "which" remembers which cpu set the stop flag. If multiple // cpus set stop, too bad. } // increment time tick only after all processors have had their chance. BX_TICKN(quantum); } #endif // (mch) hack bx_vga.timer_handler(&bx_vga); BX_INSTR_DEBUG_PROMPT(); bx_dbg_print_guard_results(); if (BX_CPU(which)->stop_reason == STOP_TRACE || BX_CPU(which)->stop_reason == STOP_CPU_HALTED) goto one_more; if (watchpoint_continue && (BX_CPU(which)->stop_reason == STOP_READ_WATCH_POINT || BX_CPU(which)->stop_reason == STOP_WRITE_WATCH_POINT)) goto one_more; } void bx_dbg_stepN_command(bx_dbg_icount_t count) { if (count == 0) { fprintf(stderr, "Error: stepN: count=0\n"); return; } #if BX_NUM_SIMULATORS >= 2 bx_guard.interrupt_requested = 0; bx_dbg_cosimulateN(count); #else // single CPU bx_guard.guard_for |= BX_DBG_GUARD_ICOUNT; // looking for icount bx_guard.guard_for |= BX_DBG_GUARD_CTRL_C; // or Ctrl-C // for now, step each CPU one BX_DBG_DEFAULT_ICOUNT_QUANTUM at a time //BX_INFO(("Stepping each CPU a total of %d cycles", count)); for (unsigned cycle=0; cycle < count; cycle++) { for (unsigned cpu=0; cpu < BX_SMP_PROCESSORS; cpu++) { //BX_INFO(("Stepping %s", BX_CPU(cpu)->name)); bx_guard.icount = 1; bx_guard.interrupt_requested = 0; BX_CPU(cpu)->guard_found.guard_found = 0; BX_CPU(cpu)->guard_found.icount = 0; BX_CPU(cpu)->cpu_loop(-1); } BX_TICK1 (); } //BX_INFO(("Stepped each CPU a total of %d cycles", count)); #endif BX_INSTR_DEBUG_PROMPT(); bx_dbg_print_guard_results(); } #if BX_NUM_SIMULATORS >= 2 unsigned bx_dbg_cosimulateN(bx_dbg_icount_t count) { // execute both master & slave for count instructions, // handling asynchronous events, etc. // returns 0 = didn't get through all count instructions // either a guard was hit, or a divergence occurred // 1 = got through all count instructions unsigned master, slave; bx_dbg_icount_t master_icount, slave_icount; Boolean bail_out = 0; unsigned ret = 0; Boolean save_INTR; Boolean pre_A20, post_A20; unsigned async_head; bx_dbg_icount_t async_icount, curr_icount; if (count == 0) { fprintf(stderr, "Error: cosimulateN: count=0\n"); bx_dbg_exit(1); } bx_guard.guard_for |= BX_DBG_GUARD_ICOUNT; // stop at icount bx_guard.guard_for &= ~BX_DBG_GUARD_CTRL_C; // ignore Ctrl-C one_time_slice: // take minimum of requested count and maximum count quantum if (count > bx_debugger.icount_quantum) bx_guard.icount = bx_debugger.icount_quantum; else bx_guard.icount = count; // for now, assume... master = bx_debugger.master; slave = bx_debugger.slave; // run master simulator bx_debugger.master_slave_mode = BX_DBG_MASTER_MODE; if (bx_guard.interrupt_requested) { bail_out = 1; fprintf(stderr, "ctrlc typed\n"); } bx_guard_found[master].guard_found = 0; bx_guard_found[master].icount = 0; if (doit) fprintf(stderr, "requesting run of master for %u\n", (unsigned) bx_guard.icount); // save A20 value before master run pre_A20 = bx_pc_system.get_enable_a20(); BX_MEM(master)->cpu_loop(-1); post_A20 = bx_pc_system.get_enable_a20(); // A20 after master run master_icount = bx_guard_found[master].icount; slave_icount = 0; if (master_icount) bx_pc_system.tickn(master_icount); save_INTR = bx_pc_system.INTR; // value after master run bx_pc_system.INTR = 0; // in case slave uses directly // Change A20 for slave run to model what it was at beginning of // master run, only if it needs to be changed. if (pre_A20 != post_A20) { bx_pc_system.set_enable_a20(pre_A20); if (BX_MEM(slave)->set_A20) BX_MEM(slave)->set_A20(pre_A20); } // if guard was anything except for icount, we should terminate // after synchronizing slave to master if (bx_guard_found[master].guard_found & ~BX_DBG_GUARD_ICOUNT) bail_out = 1; // Synchronize slave to master. Account for Ctrl-C's typed during execution of // slave. bx_debugger.master_slave_mode = BX_DBG_SLAVE_MODE; do { // run slave for remaining instructions to catch up to master curr_icount = master_icount - slave_icount; if (bx_debugger.async_journal.size) { // If there were asynchronous events which occurred while the // master was running, have to run the slave up to each of these // points individually, and force it to take them on exactly the // same boundaries. async_head = bx_debugger.async_journal.head; async_icount = bx_debugger.async_journal.element[async_head].icount; curr_icount = async_icount; // only run to next async event } else { async_head = 0; // keep compiler happy async_icount = 0; // keep compiler happy } bx_guard_found[slave].guard_found = 0; bx_guard_found[slave].icount = 0; bx_guard.icount = curr_icount; if (curr_icount) { // Async event may be before completion of any instructions, // for example taking of interrupt. if (doit) fprintf(stderr, "requesting run of slave for %u\n", (unsigned) bx_guard.icount); if (bx_debugger.fast_forward_mode) { bx_guard_found[slave].icount = curr_icount; bx_guard_found[slave].guard_found = BX_DBG_GUARD_ICOUNT; } else { BX_MEM(slave)->cpu_loop(-1); } } slave_icount += bx_guard_found[slave].icount; if (bx_guard_found[slave].guard_found & ~BX_DBG_GUARD_ICOUNT) { bail_out = 1; // If user type Ctrl-C we're done after synchronizing. If not, // then we have reached a true guard, and it's time to bail. if (bx_guard_found[slave].guard_found & ~(BX_DBG_GUARD_ICOUNT | BX_DBG_GUARD_CTRL_C)) break; } if (bx_debugger.async_journal.size) { // sanity check: slave should be at async point if (bx_guard_found[slave].icount != async_icount) { fprintf(stderr, "Error: comsimulateN: async: slave not at sync point.\n"); bx_dbg_exit(1); } switch (bx_debugger.async_journal.element[async_head].what) { case BX_DBG_ASYNC_JOURNAL_IAC: if (!bx_debugger.fast_forward_mode) { if (doit) fprintf(stderr, "slave: forcing interrupt %u\n", bx_debugger.async_journal.element[async_head].u.iac.val); BX_MEM(slave)->dbg_force_interrupt( bx_debugger.async_journal.element[async_head].u.iac.val); } break; case BX_DBG_ASYNC_JOURNAL_A20: bx_pc_system.set_enable_a20( bx_debugger.async_journal.element[async_head].u.a20.val); if (BX_MEM(slave)->set_A20) BX_MEM(slave)->set_A20( bx_debugger.async_journal.element[async_head].u.a20.val); break; case BX_DBG_ASYNC_JOURNAL_NMI: case BX_DBG_ASYNC_JOURNAL_RESET: default: fprintf(stderr, "Error: cosimulateN: unimplemented async event.\n"); } // async event processed, dequeue it bx_debugger.async_journal.size--; bx_debugger.async_journal.head++; } } while (slave_icount < master_icount); bx_pc_system.INTR = save_INTR; // restore INTR to value after master run // At this point, both simulators should be at the same point. Either // they have finished executing for the desired count, or at least for // a time quantum. Check to see if the environments are in sync. int iaddr_res; if (!bx_debugger.fast_forward_mode) { if (bx_debugger.compare_at_sync.iaddr && (iaddr_res = bx_dbg_compare_sim_iaddr())) { if (iaddr_res == 1) bail_out = 1; } else if (bx_debugger.compare_at_sync.cpu && bx_dbg_compare_sim_cpu()) bail_out = 1; else if (bx_debugger.compare_at_sync.memory && bx_dbg_compare_sim_memory()) bail_out = 1; } if (bail_out) { #ifdef DEBUGGER_ERROR extern void DEBUGGER_ERROR(void); DEBUGGER_ERROR(); #endif ret = 0; // didn't complete, stopped } else { count -= master_icount; // last icount known to be in sync bx_debugger.last_sync_icount += master_icount; if (count) goto one_time_slice; ret = 1; // completed OK } bx_guard.guard_for &= ~BX_DBG_GUARD_ICOUNT; return(ret); } #endif // #if BX_NUM_SIMULATORS >= 2 #if BX_NUM_SIMULATORS >= 2 int bx_dbg_compare_sim_iaddr(void) { // returns 0 = same, 1 = different, 2 = false diff if (BX_CPU(dbg_cpu)->guard_found.laddr != bx_guard_found[1].laddr) { #ifdef FALSE_DIFF_DETECT extern int FALSE_DIFF_DETECT(); if (FALSE_DIFF_DETECT()) return 2; #endif fprintf(stderr, #if BX_DBG_ICOUNT_SIZE == 32 "*** Iaddr divergence ***: last know synchronized icount was %lu\n", (unsigned long) bx_debugger.last_sync_icount #else // BX_DBG_ICOUNT_SIZE == 64 "*** Iaddr divergence ***: last know synchronized icount was %Lu\n", (unsigned long long) bx_debugger.last_sync_icount #endif ); // fprintf(stderr, "Divergence: sim[0].laddr=%x, sim[1].laddr=%x\n", // (unsigned) BX_CPU(dbg_cpu)->guard_found.laddr, // (unsigned) bx_guard_found[1].laddr); return(1); // different } return(0); // same } Boolean bx_dbg_compare_sim_cpu(void) { // (mch) Get cpu structures from both simulators // Compare the structures (except the descriptor parts of the // segment registers bx_dbg_cpu_t regs[2]; BX_MEM(0)->dbg_get_cpu(regs + 0); BX_MEM(1)->dbg_get_cpu(regs + 1); Boolean ret = 0; Boolean warn = 0; // (mch) Yes I know these are macros. The would have been // inner functions if g++ had supported it. #define TEST_REG(reg, reg_name) do { \ if (regs[0].reg != regs[1].reg) { \ printf("COSIM ERROR: [%s] %s: 0x%08x %s: 0x%08x\n", reg_name, SIM_NAME0, regs[0].reg, SIM_NAME1_STR, regs[1].reg); \ ret = 1; \ } \ } while(0) #define TEST_REG_WARN(reg, reg_name, mask) do { \ if ((regs[0].reg & mask) != (regs[1].reg & mask)) { \ printf("COSIM WARNING: [%s] %s: 0x%08x %s: 0x%08x\n", reg_name, SIM_NAME0, (regs[0].reg & mask), SIM_NAME1_STR, (regs[1].reg & mask)); \ warn = 1; \ } \ } while(0) TEST_REG(eax, "eax"); TEST_REG(ebx, "ebx"); TEST_REG(ecx, "ecx"); TEST_REG(edx, "edx"); TEST_REG(ebp, "ebp"); TEST_REG(esi, "esi"); TEST_REG(edi, "edi"); TEST_REG(esp, "esp"); TEST_REG_WARN(eflags, "eflags & CF", 0x1); #define EFLAGS_MASK (~((1 << 11) | (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 0))) regs[0].eflags &= EFLAGS_MASK; regs[1].eflags &= EFLAGS_MASK; TEST_REG(eflags, "eflags"); TEST_REG(eip, "eip"); #define TEST_SEG_REG(reg, reg_name) do { \ if (regs[0].reg.sel != regs[1].reg.sel || regs[0].reg.valid != regs[1].reg.valid) { \ printf("COSIM ERROR: [%s] %s: 0x%04x (%d) %s: 0x%04x (%d)\n", reg_name, SIM_NAME0, regs[0].reg.sel, regs[0].reg.valid, SIM_NAME1_STR, regs[1].reg.sel, regs[1].reg.valid); \ ret = 1; \ } \ } while(0) TEST_SEG_REG(cs, "cs"); TEST_SEG_REG(ss, "ss"); TEST_SEG_REG(ds, "ds"); TEST_SEG_REG(es, "es"); TEST_SEG_REG(fs, "fs"); TEST_SEG_REG(gs, "gs"); TEST_SEG_REG(ldtr, "ldtr"); TEST_SEG_REG(tr, "tr"); if (regs[0].gdtr.base != regs[1].gdtr.base || regs[0].gdtr.limit != regs[1].gdtr.limit) { printf("COSIM ERROR: [gdtr] %s: 0x%08x:0x%04x %s 0x%08x:0x%04x\n", SIM_NAME0, regs[0].gdtr.base, regs[0].gdtr.limit, SIM_NAME1_STR, regs[1].gdtr.base, regs[1].gdtr.limit); ret = 1; } if (regs[0].idtr.base != regs[1].idtr.base || regs[0].idtr.limit != regs[1].idtr.limit) { printf("COSIM ERROR: [idtr] %s: 0x%08x:0x%04x %s 0x%08x:0x%04x\n", SIM_NAME0, regs[0].idtr.base, regs[0].idtr.limit, SIM_NAME1_STR, regs[1].idtr.base, regs[1].idtr.limit); ret = 1; } // drX ignored // trX ignored TEST_REG(cr0, "cr0"); TEST_REG(cr1, "cr1"); TEST_REG(cr2, "cr2"); TEST_REG(cr3, "cr3"); TEST_REG(cr4, "cr4"); if (regs[0].inhibit_mask != regs[1].inhibit_mask) { printf("COSIM ERROR [inhibit_mask] %s: %d %s: %d\n", SIM_NAME0, regs[0].inhibit_mask, SIM_NAME1_STR, regs[1].inhibit_mask); ret = 1; } if (ret) { fprintf(stderr, #if BX_DBG_ICOUNT_SIZE == 32 "*** CPU divergence ***: last know synchronized icount was %lu\n", (unsigned long) bx_debugger.last_sync_icount #else // BX_DBG_ICOUNT_SIZE == 64 "*** CPU divergence ***: last know synchronized icount was %Lu\n", (unsigned long long) bx_debugger.last_sync_icount #endif ); } else if (warn) { fprintf(stderr, #if BX_DBG_ICOUNT_SIZE == 32 "=== CPU divergence ===: last know synchronized icount was %lu\n", (unsigned long) bx_debugger.last_sync_icount #else // BX_DBG_ICOUNT_SIZE == 64 "=== CPU divergence ===: last know synchronized icount was %Lu\n", (unsigned long long) bx_debugger.last_sync_icount #endif ); #ifdef DEBUGGER_ERROR extern void DEBUGGER_ERROR(void); DEBUGGER_ERROR(); #endif } return ret; } void clear_dirty_bits (void) { int num_pages = bx_options.memory.Osize->get () * 1024 / 4; for (int i = 0; i < num_pages; i++) { BX_MEM(0)->dbg_dirty_pages[i] = 0; BX_MEM(1)->dbg_dirty_pages[i] = 0; } } Boolean always_check_page[128 * 1024 / 4]; void bx_dbg_always_check(Bit32u page_start, Boolean on) { always_check_page[page_start / (4 * 1024)] = on; printf("Forced check on page %08x %s\n", page_start, on ? "enabled" : "disabled"); } Boolean bx_dbg_compare_sim_memory(void) { Boolean ret = 0; int num_pages = bx_options.memory.Osize->get () * 1024 / 4; for (int i = 0; i < num_pages; i++) { Boolean sim0_dirty = BX_MEM(0)->dbg_dirty_pages[i]; Boolean sim1_dirty = BX_MEM(1)->dbg_dirty_pages[i]; Bit32u page_start = i * 1024 * 4; if ((sim0_dirty != sim1_dirty) || sim0_dirty || always_check_page[i]) { // Page has been written, compare // (mch) I'm quite aware of how hackish this is. I don't care. extern Bit8u* SIM1_GET_PHYS_PTR(Bit32u page_start); Bit8u* sim0_page_vec = bx_mem0.vector + page_start; Bit8u* sim1_page_vec = SIM1_GET_PHYS_PTR(page_start); if (memcmp(sim0_page_vec, sim1_page_vec, 1024 * 4)) { printf("COSIM ERROR Physical page %08x differs in content\n", page_start); for (int j = 0; j < 1024 * 4; j++) { if (sim0_page_vec[j] != sim1_page_vec[j]) { printf("%08x %s: %02x %s: %02x\n", page_start+j, SIM_NAME0, sim0_page_vec[j], SIM_NAME1_STR, sim1_page_vec[j]); } } ret = 1; } } } if (ret) { fprintf(stderr, #if BX_DBG_ICOUNT_SIZE == 32 "*** Memory divergence ***: last know synchronized icount was %lu\n", (unsigned long) bx_debugger.last_sync_icount #else // BX_DBG_ICOUNT_SIZE == 64 "*** Memory divergence ***: last know synchronized icount was %Lu\n", (unsigned long long) bx_debugger.last_sync_icount #endif ); } clear_dirty_bits(); return ret; } #endif void bx_dbg_print_guard_results(void) { unsigned i; unsigned sim; for (sim=0; simguard_found.guard_found; if (found & BX_DBG_GUARD_ICOUNT) { } else if (found & BX_DBG_GUARD_CTRL_C) { } #if BX_DBG_SUPPORT_VIR_BPOINT else if (found & BX_DBG_GUARD_IADDR_VIR) { i = BX_CPU(sim)->guard_found.iaddr_index; fprintf(stderr, "(%u) Breakpoint %u, 0x%x (0x%x:0x%x)\n", sim, bx_guard.iaddr.vir[i].bpoint_id, BX_CPU(sim)->guard_found.laddr, BX_CPU(sim)->guard_found.cs, BX_CPU(sim)->guard_found.eip); } #endif #if BX_DBG_SUPPORT_LIN_BPOINT else if (found & BX_DBG_GUARD_IADDR_LIN) { i = BX_CPU(sim)->guard_found.iaddr_index; fprintf(stderr, "(%u) Breakpoint %u, 0x%x in ?? ()\n", sim, bx_guard.iaddr.lin[i].bpoint_id, BX_CPU(sim)->guard_found.laddr); } #endif #if BX_DBG_SUPPORT_PHY_BPOINT else if (found & BX_DBG_GUARD_IADDR_PHY) { i = BX_CPU(sim)->guard_found.iaddr_index; fprintf(stderr, "(%u) Breakpoint %u, 0x%x in ?? ()\n", sim, bx_guard.iaddr.phy[i].bpoint_id, BX_CPU(sim)->guard_found.laddr); } #endif else if (BX_CPU(sim)->stop_reason == STOP_CPU_HALTED) { /* returned early because processor is in halt state */ } else if (BX_CPU(sim)->stop_reason == STOP_MAGIC_BREAK_POINT) { fprintf(stderr, "(%u) Magic breakpoint\n", sim); } else if (BX_CPU(sim)->stop_reason == STOP_TRACE) { /* Nothing */ } else if (BX_CPU(sim)->stop_reason == STOP_TIME_BREAK_POINT) { fprintf(stderr, "(%u) Caught time breakpoint\n", sim); } else if (BX_CPU(sim)->stop_reason == STOP_MODE_BREAK_POINT) { fprintf(stderr, "(%u) Caught vm mode switch breakpoint to %s mode\n", sim, BX_CPU(sim)->eflags.vm ? "virtual 86" : "protected"); } else if (BX_CPU(sim)->stop_reason == STOP_READ_WATCH_POINT) { fprintf(stderr, "(%u) Caught read watch point\n", sim); } else if (BX_CPU(sim)->stop_reason == STOP_WRITE_WATCH_POINT) { fprintf(stderr, "(%u) Caught write watch point\n", sim); } else { fprintf(stderr, "Error: (%u) print_guard_results: guard_found ? (stop reason %u)\n", sim, BX_CPU(sim)->stop_reason); } #if BX_DISASM if (bx_debugger.auto_disassemble) { Bit32u phy; Boolean valid; BX_CPU(sim)->dbg_xlate_linear2phy(BX_CPU(sim)->guard_found.laddr, &phy, &valid); if (valid) { unsigned ilen; BX_CPU(sim)->mem->dbg_fetch_mem(phy, 16, bx_disasm_ibuf); ilen = bx_disassemble.disasm(BX_CPU(sim)->guard_found.is_32bit_code, bx_disasm_ibuf, bx_disasm_tbuf); if (BX_CPU(sim)->guard_found.is_32bit_code) { fprintf(stderr, "(%u) %04x:%08x (%s): ", sim, (unsigned) BX_CPU(sim)->guard_found.cs, (unsigned) BX_CPU(sim)->guard_found.eip, bx_dbg_symbolic_address((BX_CPU(sim)->cr3) >> 12, BX_CPU(sim)->guard_found.eip, BX_CPU(sim)->sregs[BX_SREG_CS].cache.u.segment.base)); } else { fprintf(stderr, "(%u) %04x:%04x: ", sim, (unsigned) BX_CPU(sim)->guard_found.cs, (unsigned) BX_CPU(sim)->guard_found.eip); } for (unsigned j=0; j= BX_DBG_MAX_VIR_BPOINTS) { fprintf(stderr, "Error: no more virtual breakpoint slots left.\n"); fprintf(stderr, "Error: see BX_DBG_MAX_VIR_BPOINTS.\n"); return; } bx_guard.iaddr.vir[bx_guard.iaddr.num_virtual].cs = cs; bx_guard.iaddr.vir[bx_guard.iaddr.num_virtual].eip = eip; bx_guard.iaddr.vir[bx_guard.iaddr.num_virtual].bpoint_id = bx_debugger.next_bpoint_id++; bx_guard.iaddr.num_virtual++; bx_guard.guard_for |= BX_DBG_GUARD_IADDR_VIR; #else fprintf(stderr, "Error: virtual breakpoint support not compiled in.\n"); fprintf(stderr, "Error: see BX_DBG_SUPPORT_VIR_BPOINT.\n"); #endif } void bx_dbg_lbreakpoint_command(Boolean specific, Bit32u laddress) { #if BX_DBG_SUPPORT_LIN_BPOINT if (specific == 0) { fprintf(stderr, "Error: lbreak without address not implemented yet.\n"); return; } if (bx_guard.iaddr.num_linear >= BX_DBG_MAX_LIN_BPOINTS) { fprintf(stderr, "Error: no more linear breakpoint slots left.\n"); fprintf(stderr, "Error: see BX_DBG_MAX_LIN_BPOINTS.\n"); return; } bx_guard.iaddr.lin[bx_guard.iaddr.num_linear].addr = laddress; bx_guard.iaddr.lin[bx_guard.iaddr.num_linear].bpoint_id = bx_debugger.next_bpoint_id++; bx_guard.iaddr.num_linear++; bx_guard.guard_for |= BX_DBG_GUARD_IADDR_LIN; #else fprintf(stderr, "Error: linear breakpoint support not compiled in.\n"); fprintf(stderr, "Error: see BX_DBG_SUPPORT_LIN_BPOINT.\n"); #endif } void bx_dbg_pbreakpoint_command(Boolean specific, Bit32u paddress) { #if BX_DBG_SUPPORT_PHY_BPOINT if (specific == 0) { fprintf(stderr, "Error: pbreak without address not implemented yet.\n"); return; } if (bx_guard.iaddr.num_physical >= BX_DBG_MAX_PHY_BPOINTS) { fprintf(stderr, "Error: no more physical breakpoint slots left.\n"); fprintf(stderr, "Error: see BX_DBG_MAX_PHY_BPOINTS.\n"); return; } bx_guard.iaddr.phy[bx_guard.iaddr.num_physical].addr = paddress; bx_guard.iaddr.phy[bx_guard.iaddr.num_physical].bpoint_id = bx_debugger.next_bpoint_id++; bx_guard.iaddr.num_physical++; bx_guard.guard_for |= BX_DBG_GUARD_IADDR_PHY; #else fprintf(stderr, "Error: physical breakpoint support not compiled in.\n"); fprintf(stderr, "Error: see BX_DBG_SUPPORT_PHY_BPOINT.\n"); #endif } void bx_dbg_info_bpoints_command(void) { unsigned i; // Num Type Disp Enb Address What // 1 breakpoint keep y 0x00010664 in main at temp.c:7 fprintf(stderr, "Num Type Disp Enb Address\n"); #if BX_DBG_SUPPORT_VIR_BPOINT for (i=0; idbg_take_dma(); } bx_dbg_batch_dma.this_many = 1; // reset to normal bx_dbg_post_dma_reports(); // print reports and flush if (bx_guard.report.dma) fprintf(stderr, "done\n"); } else if ( !strcmp(what, "irq") ) { BX_CPU(0)->dbg_take_irq(); if (bx_guard.report.irq) fprintf(stderr, "done\n"); } else { fprintf(stderr, "Error: Take '%s' not understood.\n", what); } } void bx_dbg_info_registers_command(void) { Bit32u reg; bx_dbg_cpu_t cpu; for (unsigned i=0; idbg_get_cpu(&cpu); #if (BX_SMP_PROCESSORS > 1) fprintf(stderr, "%s:\n", BX_CPU(i)->name, i); #endif reg = cpu.eax; fprintf(stderr, "eax 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.ecx; fprintf(stderr, "ecx 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.edx; fprintf(stderr, "edx 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.ebx; fprintf(stderr, "ebx 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.esp; fprintf(stderr, "esp 0x%-8x\t0x%-8x\n", (unsigned) reg, (int) reg); reg = cpu.ebp; fprintf(stderr, "ebp 0x%-8x\t0x%-8x\n", (unsigned) reg, (int) reg); reg = cpu.esi; fprintf(stderr, "esi 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.edi; fprintf(stderr, "edi 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.eip; fprintf(stderr, "eip 0x%-8x\t0x%-8x\n", (unsigned) reg, (int) reg); reg = cpu.eflags; fprintf(stderr, "eflags 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.cs.sel; fprintf(stderr, "cs 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.ss.sel; fprintf(stderr, "ss 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.ds.sel; fprintf(stderr, "ds 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.es.sel; fprintf(stderr, "es 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.fs.sel; fprintf(stderr, "fs 0x%-8x\t%d\n", (unsigned) reg, (int) reg); reg = cpu.gs.sel; fprintf(stderr, "gs 0x%-8x\t%d\n", (unsigned) reg, (int) reg); } } void bx_dbg_info_program_command(void) { fprintf(stderr, " Using the running image of child process -1.\n"); fprintf(stderr, "Program stopped at 0x0.\n"); fprintf(stderr, "It stopped at breakpoint 0.\n"); } void bx_dbg_dump_cpu_command(void) { bx_dbg_cpu_t cpu; for (unsigned i=0; idbg_get_cpu(&cpu); #if (BX_SMP_PROCESSORS >= 2) fprintf(stderr, "CPU#%u\n", i); #endif fprintf(stderr, "eax:0x%x\n", (unsigned) cpu.eax); fprintf(stderr, "ebx:0x%x\n", (unsigned) cpu.ebx); fprintf(stderr, "ecx:0x%x\n", (unsigned) cpu.ecx); fprintf(stderr, "edx:0x%x\n", (unsigned) cpu.edx); fprintf(stderr, "ebp:0x%x\n", (unsigned) cpu.ebp); fprintf(stderr, "esi:0x%x\n", (unsigned) cpu.esi); fprintf(stderr, "edi:0x%x\n", (unsigned) cpu.edi); fprintf(stderr, "esp:0x%x\n", (unsigned) cpu.esp); fprintf(stderr, "eflags:0x%x\n", (unsigned) cpu.eflags); fprintf(stderr, "eip:0x%x\n", (unsigned) cpu.eip); fprintf(stderr, "cs:s=0x%x, dl=0x%x, dh=0x%x, valid=%u\n", (unsigned) cpu.cs.sel, (unsigned) cpu.cs.des_l, (unsigned) cpu.cs.des_h, (unsigned) cpu.cs.valid); fprintf(stderr, "ss:s=0x%x, dl=0x%x, dh=0x%x, valid=%u\n", (unsigned) cpu.ss.sel, (unsigned) cpu.ss.des_l, (unsigned) cpu.ss.des_h, (unsigned) cpu.ss.valid); fprintf(stderr, "ds:s=0x%x, dl=0x%x, dh=0x%x, valid=%u\n", (unsigned) cpu.ds.sel, (unsigned) cpu.ds.des_l, (unsigned) cpu.ds.des_h, (unsigned) cpu.ds.valid); fprintf(stderr, "es:s=0x%x, dl=0x%x, dh=0x%x, valid=%u\n", (unsigned) cpu.es.sel, (unsigned) cpu.es.des_l, (unsigned) cpu.es.des_h, (unsigned) cpu.es.valid); fprintf(stderr, "fs:s=0x%x, dl=0x%x, dh=0x%x, valid=%u\n", (unsigned) cpu.fs.sel, (unsigned) cpu.fs.des_l, (unsigned) cpu.fs.des_h, (unsigned) cpu.fs.valid); fprintf(stderr, "gs:s=0x%x, dl=0x%x, dh=0x%x, valid=%u\n", (unsigned) cpu.gs.sel, (unsigned) cpu.gs.des_l, (unsigned) cpu.gs.des_h, (unsigned) cpu.gs.valid); fprintf(stderr, "ldtr:s=0x%x, dl=0x%x, dh=0x%x, valid=%u\n", (unsigned) cpu.ldtr.sel, (unsigned) cpu.ldtr.des_l, (unsigned) cpu.ldtr.des_h, (unsigned) cpu.ldtr.valid); fprintf(stderr, "tr:s=0x%x, dl=0x%x, dh=0x%x, valid=%u\n", (unsigned) cpu.tr.sel, (unsigned) cpu.tr.des_l, (unsigned) cpu.tr.des_h, (unsigned) cpu.tr.valid); fprintf(stderr, "gdtr:base=0x%x, limit=0x%x\n", (unsigned) cpu.gdtr.base, (unsigned) cpu.gdtr.limit); fprintf(stderr, "idtr:base=0x%x, limit=0x%x\n", (unsigned) cpu.idtr.base, (unsigned) cpu.idtr.limit); fprintf(stderr, "dr0:0x%x\n", (unsigned) cpu.dr0); fprintf(stderr, "dr1:0x%x\n", (unsigned) cpu.dr1); fprintf(stderr, "dr2:0x%x\n", (unsigned) cpu.dr2); fprintf(stderr, "dr3:0x%x\n", (unsigned) cpu.dr3); fprintf(stderr, "dr6:0x%x\n", (unsigned) cpu.dr6); fprintf(stderr, "dr7:0x%x\n", (unsigned) cpu.dr7); fprintf(stderr, "tr3:0x%x\n", (unsigned) cpu.tr3); fprintf(stderr, "tr4:0x%x\n", (unsigned) cpu.tr4); fprintf(stderr, "tr5:0x%x\n", (unsigned) cpu.tr5); fprintf(stderr, "tr6:0x%x\n", (unsigned) cpu.tr6); fprintf(stderr, "tr7:0x%x\n", (unsigned) cpu.tr7); fprintf(stderr, "cr0:0x%x\n", (unsigned) cpu.cr0); fprintf(stderr, "cr1:0x%x\n", (unsigned) cpu.cr1); fprintf(stderr, "cr2:0x%x\n", (unsigned) cpu.cr2); fprintf(stderr, "cr3:0x%x\n", (unsigned) cpu.cr3); fprintf(stderr, "cr4:0x%x\n", (unsigned) cpu.cr4); fprintf(stderr, "inhibit_mask:%u\n", cpu.inhibit_mask); } #if BX_PCI_SUPPORT if (bx_options.Oi440FXSupport->get ()) { bx_devices.pci->print_i440fx_state(); } #endif fprintf(stderr, "done\n"); } void bx_dbg_examine_command(char *command, char *format, Boolean format_passed, Bit32u addr, Boolean addr_passed, int simulator) { unsigned repeat_count, i; char ch, display_format, unit_size; Boolean iteration; unsigned data_size; Boolean paddr_valid; Bit32u paddr; Bit8u data8; Bit16u data16; Bit32u data32; unsigned columns, per_line, offset; unsigned char digit; unsigned biti; Boolean is_linear; unsigned char databuf[8]; if (simulator == 0) printf("[%s]:\n", SIM_NAME0); else printf("[%s]:\n", SIM_NAME1_STR); // If command was the extended "xp" command, meaning eXamine Physical memory, // then flag memory address as physical, rather than linear. if (strcmp(command, "xp") == 0) { is_linear = 0; } else { is_linear = 1; } if (addr_passed==0) addr = bx_debugger.default_addr; if (format_passed==0) { display_format = bx_debugger.default_display_format; unit_size = bx_debugger.default_unit_size; repeat_count = 1; } else { if (format==NULL) { fprintf(stderr, "dbg_examine: format NULL\n"); bx_dbg_exit(1); } if (strlen(format) < 2) { fprintf(stderr, "dbg_examine: invalid format passed.\n"); bx_dbg_exit(1); } if (format[0] != '/') { fprintf(stderr, "dbg_examine: '/' is not first char of format.\n"); bx_dbg_exit(1); } format++; repeat_count = 0; ch = *format; iteration = 0; while ( (ch>='0') && (ch<='9') ) { iteration = 1; repeat_count = 10*repeat_count + (ch-'0'); format++; ch = *format; } if (iteration==0) { // if no count given, use default repeat_count = 1; } else if (repeat_count==0) { // count give, but zero is an error fprintf(stderr, "dbg_examine: repeat count given but is zero.\n"); return; } // set up the default display format and unit size parameters display_format = bx_debugger.default_display_format; unit_size = bx_debugger.default_unit_size; for (i=0; i<=1; i++) { if (ch==0) break; // bail on null character switch (ch) { case 'x': // hex case 'd': // signed decimal case 'u': // unsigned decimal case 'o': // octal case 't': // binary case 'c': // chars case 's': // null terminated string case 'i': // machine instruction display_format = ch; break; case 'b': // bytes case 'h': // halfwords (two bytes) case 'w': // words (4 bytes) case 'g': // giant words (8 bytes) unit_size = ch; break; default: fprintf(stderr, "dbg_examine: invalid format passed.\n"); bx_dbg_exit(1); break; } format++; ch = *format; } // store current options as default bx_debugger.default_display_format = display_format; bx_debugger.default_unit_size = unit_size; } //fprintf(stderr, " repeat count was %u\n", repeat_count); //fprintf(stderr, " display_format = '%c'\n", display_format); //fprintf(stderr, " unit_size = '%c'\n", unit_size); if ( (display_format == 'i') || (display_format == 's') ) { fprintf(stderr, "error: dbg_examine: 'i' and 's' formats not supported.\n"); return; } if (unit_size == 'g') { fprintf(stderr, "error: dbg_examine: 'g' (8-byte) unit size not supported.\n"); return; } data_size = 0; per_line = 0; offset = 0; switch (unit_size) { case 'b': data_size = 1; per_line = 8; break; case 'h': data_size = 2; per_line = 8; break; case 'w': data_size = 4; per_line = 4; break; //case 'g': data_size = 8; per_line = 2; break; } columns = per_line + 1; // set current number columns past limit for (i=1; i<=repeat_count; i++) { if (columns > per_line) { // if not 1st run, need a newline from last line if (i!=1) fprintf(stderr, "\n"); fprintf(stderr, "0x%x :", addr, offset); columns = 1; } if (is_linear) { BX_CPU(simulator)->dbg_xlate_linear2phy(addr, &paddr, &paddr_valid); if (!paddr_valid) { fprintf(stderr, "error: examine memory: no tranlation for linear-to-phy mem available.\n"); return; } } else { paddr = addr; // address is already physical address } BX_MEM(simulator)->dbg_fetch_mem(paddr, data_size, databuf); //FIXME HanishKVC The char display for data to be properly integrated // so that repeat_count, columns, etc. can be set or used properly. // Also for data_size of 2 and 4 how to display the individual // characters i.e in which order to be decided. switch (data_size) { case 1: data8 = databuf[0]; switch (display_format) { case 'x': fprintf(stderr, "\t0x%02x", (unsigned) data8); break; case 'd': fprintf(stderr, "\t%d", (int) (Bit8s) data8); break; case 'u': fprintf(stderr, "\t%u", (unsigned) data8); break; case 'o': fprintf(stderr, "\t%o", (unsigned) data8); break; case 't': fputc('\t', stderr); for (biti=7; ; biti--) { digit = (data8 >> biti) & 0x01; fputc(digit + '0', stderr); if (biti==0) break; } break; case 'c': fprintf(stderr, " %c",data8); break; } break; case 2: #ifdef BX_LITTLE_ENDIAN data16 = * (Bit16u *) databuf; #else data16 = (databuf[1]<<8) | databuf[0]; #endif switch (display_format) { case 'x': fprintf(stderr, "\t0x%04x", (unsigned) data16); break; case 'd': fprintf(stderr, "\t%d", (int) (Bit16s) data16); break; case 'u': fprintf(stderr, "\t%u", (unsigned) data16); break; case 'o': fprintf(stderr, "\t%o", (unsigned) data16); break; case 't': fputc('\t', stderr); for (biti=15; ; biti--) { digit = (data16 >> biti) & 0x01; fputc(digit + '0', stderr); if (biti==0) break; } break; case 'c': fprintf(stderr, " %c %c",data16>>8,data16&0xff); break; } break; case 4: #ifdef BX_LITTLE_ENDIAN data32 = * (Bit32u *) databuf; #else data32 = (databuf[3]<<24) | (databuf[2]<<16) | (databuf[1]<<8) | databuf[0]; #endif switch (display_format) { case 'x': fprintf(stderr, "\t0x%08x", (unsigned) data32); break; case 'd': fprintf(stderr, "\t%d", (int) (Bit32s) data32); break; case 'u': fprintf(stderr, "\t%u", (unsigned) data32); break; case 'o': fprintf(stderr, "\t%o", (unsigned) data32); break; case 't': fputc('\t', stderr); for (biti=31; ; biti--) { digit = (data32 >> biti) & 0x01; fputc(digit + '0', stderr); if (biti==0) break; } break; case 'c': fprintf(stderr, " %c %c",data32>>24,(data32>>16)&0xff); fprintf(stderr, " %c %c",(data32>>8)&0xff,data32&0xff); break; } break; } addr += data_size; bx_debugger.default_addr = addr; columns++; offset += data_size; } fprintf(stderr, "\n"); } void bx_dbg_setpmem_command(Bit32u addr, unsigned len, Bit32u val) { Boolean is_OK; Bit8u buf[4]; switch ( len ) { case 1: buf[0] = (Bit8u) val; break; case 2: buf[0] = val & 0xff; buf[1] = (val>>8) & 0xff; break; case 4: buf[0] = val & 0xff; val >>= 8; buf[1] = val & 0xff; val >>= 8; buf[2] = val & 0xff; val >>= 8; buf[3] = val & 0xff; break; default: fprintf(stderr, "Error: setpmem: bad length value = %u\n", len); return; } is_OK = BX_MEM(0)->dbg_set_mem(addr, len, buf); if (!is_OK) { fprintf(stderr, "Error: setpmem: could not set memory, out of physical bounds?\n"); } } void bx_dbg_set_symbol_command(char *symbol, Bit32u val) { Boolean is_OK; symbol++; // get past '$' if ( !strcmp(symbol, "eax") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_EAX, val); } else if ( !strcmp(symbol, "ecx") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_ECX, val); } else if ( !strcmp(symbol, "edx") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_EDX, val); } else if ( !strcmp(symbol, "ebx") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_EBX, val); } else if ( !strcmp(symbol, "esp") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_ESP, val); } else if ( !strcmp(symbol, "ebp") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_EBP, val); } else if ( !strcmp(symbol, "esi") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_ESI, val); } else if ( !strcmp(symbol, "edi") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_EDI, val); } else if ( !strcmp(symbol, "eip") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_EIP, val); } else if ( !strcmp(symbol, "eflags") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_EFLAGS, val); } else if ( !strcmp(symbol, "cs") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_CS, val); } else if ( !strcmp(symbol, "ss") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_SS, val); } else if ( !strcmp(symbol, "ds") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_DS, val); } else if ( !strcmp(symbol, "es") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_ES, val); } else if ( !strcmp(symbol, "fs") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_FS, val); } else if ( !strcmp(symbol, "gs") ) { is_OK = BX_CPU(dbg_cpu)->dbg_set_reg(BX_DBG_REG_GS, val); } else if ( !strcmp(symbol, "cpu") ) { #if ((BX_SMP_PROCESSORS>1) && (BX_SUPPORT_APIC)) if ((val > BX_SMP_PROCESSORS) || (val >= APIC_MAX_ID) || (apic_index[val] == NULL)) { fprintf (stderr, "invalid cpu id number %d\n", val); return; } dbg_cpu = val; #endif } else if ( !strcmp(symbol, "synchronous_dma") ) { bx_guard.async.dma = !val; return; } else if ( !strcmp(symbol, "synchronous_irq") ) { bx_guard.async.irq = !val; return; } else if ( !strcmp(symbol, "event_reports") ) { bx_guard.report.irq = val; bx_guard.report.a20 = val; bx_guard.report.io = val; bx_guard.report.ucmem = val; bx_guard.report.dma = val; return; } else if ( !strcmp(symbol, "auto_disassemble") ) { bx_debugger.auto_disassemble = (val > 0); return; } else if ( !strcmp(symbol, "disassemble_size") ) { if ( (val!=16) && (val!=32) ) { fprintf(stderr, "Error: disassemble_size must be 16 or 32.\n"); return; } bx_debugger.disassemble_size = val; return; } else { fprintf(stderr, "Error: set: unrecognized symbol.\n"); return; } if (!is_OK) { fprintf(stderr, "Error: could not set register '%s'.\n", symbol); } } void bx_dbg_query_command(char *what) { unsigned pending; if ( !strcmp(what, "pending") ) { pending = BX_CPU(0)->dbg_query_pending(); if ( pending & BX_DBG_PENDING_DMA ) fprintf(stderr, "pending DMA\n"); if ( pending & BX_DBG_PENDING_IRQ ) fprintf(stderr, "pending IRQ\n"); if (!pending) fprintf(stderr, "pending none\n"); fprintf(stderr, "done\n"); } else { fprintf(stderr, "Error: Query '%s' not understood.\n", what); } } void bx_dbg_set_cpu_command(void) { FILE *fp; int reti; char *rets; Boolean retb; unsigned long ul1, ul2, ul3, ul4; bx_dbg_cpu_t cpu; fp = bx_infile_stack[bx_infile_stack_index].fp; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "eax:0x%lx", &ul1); cpu.eax = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "ebx:0x%lx", &ul1); cpu.ebx = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "ecx:0x%lx", &ul1); cpu.ecx = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "edx:0x%lx", &ul1); cpu.edx = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "ebp:0x%lx", &ul1); cpu.ebp = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "esi:0x%lx", &ul1); cpu.esi = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "edi:0x%lx", &ul1); cpu.edi = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "esp:0x%lx", &ul1); cpu.esp = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "eflags:0x%lx", &ul1); cpu.eflags = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "eip:0x%lx", &ul1); cpu.eip = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "cs:s=0x%lx, dl=0x%lx, dh=0x%lx, valid=%lu", &ul1, &ul2, &ul3, &ul4); cpu.cs.sel = (Bit16u) ul1; cpu.cs.des_l = ul2; cpu.cs.des_h = ul3; cpu.cs.valid = ul4; if (reti != 4) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "ss:s=0x%lx, dl=0x%lx, dh=0x%lx, valid=%lu", &ul1, &ul2, &ul3, &ul4); cpu.ss.sel = (Bit16u) ul1; cpu.ss.des_l = ul2; cpu.ss.des_h = ul3; cpu.ss.valid = ul4; if (reti != 4) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "ds:s=0x%lx, dl=0x%lx, dh=0x%lx, valid=%lu", &ul1, &ul2, &ul3, &ul4); cpu.ds.sel = (Bit16u) ul1; cpu.ds.des_l = ul2; cpu.ds.des_h = ul3; cpu.ds.valid = ul4; if (reti != 4) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "es:s=0x%lx, dl=0x%lx, dh=0x%lx, valid=%lu", &ul1, &ul2, &ul3, &ul4); cpu.es.sel = (Bit16u) ul1; cpu.es.des_l = ul2; cpu.es.des_h = ul3; cpu.es.valid = ul4; if (reti != 4) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "fs:s=0x%lx, dl=0x%lx, dh=0x%lx, valid=%lu", &ul1, &ul2, &ul3, &ul4); cpu.fs.sel = (Bit16u) ul1; cpu.fs.des_l = ul2; cpu.fs.des_h = ul3; cpu.fs.valid = ul4; if (reti != 4) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "gs:s=0x%lx, dl=0x%lx, dh=0x%lx, valid=%lu", &ul1, &ul2, &ul3, &ul4); cpu.gs.sel = (Bit16u) ul1; cpu.gs.des_l = ul2; cpu.gs.des_h = ul3; cpu.gs.valid = ul4; if (reti != 4) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "ldtr:s=0x%lx, dl=0x%lx, dh=0x%lx, valid=%lu", &ul1, &ul2, &ul3, &ul4); cpu.ldtr.sel = (Bit16u) ul1; cpu.ldtr.des_l = ul2; cpu.ldtr.des_h = ul3; cpu.ldtr.valid = ul4; if (reti != 4) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "tr:s=0x%lx, dl=0x%lx, dh=0x%lx, valid=%lu", &ul1, &ul2, &ul3, &ul4); cpu.tr.sel = (Bit16u) ul1; cpu.tr.des_l = ul2; cpu.tr.des_h = ul3; cpu.tr.valid = ul4; if (reti != 4) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "gdtr:base=0x%lx, limit=0x%lx", &ul1, &ul2); cpu.gdtr.base = ul1; cpu.gdtr.limit = ul2; if (reti != 2) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "idtr:base=0x%lx, limit=0x%lx", &ul1, &ul2); cpu.idtr.base = ul1; cpu.idtr.limit = ul2; if (reti != 2) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "dr0:0x%lx", &ul1); cpu.dr0 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "dr1:0x%lx", &ul1); cpu.dr1 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "dr2:0x%lx", &ul1); cpu.dr2 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "dr3:0x%lx", &ul1); cpu.dr3 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "dr6:0x%lx", &ul1); cpu.dr6 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "dr7:0x%lx", &ul1); cpu.dr7 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "tr3:0x%lx", &ul1); cpu.tr3 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "tr4:0x%lx", &ul1); cpu.tr4 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "tr5:0x%lx", &ul1); cpu.tr5 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "tr6:0x%lx", &ul1); cpu.tr6 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "tr7:0x%lx", &ul1); cpu.tr7 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "cr0:0x%lx", &ul1); cpu.cr0 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "cr1:0x%lx", &ul1); cpu.cr1 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "cr2:0x%lx", &ul1); cpu.cr2 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "cr3:0x%lx", &ul1); cpu.cr3 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "cr4:0x%lx", &ul1); cpu.cr4 = ul1; if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "inhibit_mask:%u", &cpu.inhibit_mask); if (reti != 1) goto scanf_error; rets = fgets(tmp_buf, 512, fp); if (!rets) goto eof_error; reti = sscanf(tmp_buf, "done"); if (reti != 0) goto scanf_error; retb = BX_CPU(0)->dbg_set_cpu(&cpu); if (retb == 0) fprintf(stderr, "Error: dbg_set_cpu encountered error\n"); else fprintf(stderr, "OK\n"); return; eof_error: fprintf(stderr, "Error: EOF encountered in dbg_set_cpu input stream\n"); return; scanf_error: fprintf(stderr, "Error: scanf returned error in dbg_set_cpu input stream\n"); return; } void bx_dbg_disassemble_command(bx_num_range range) { #if BX_DISASM Boolean paddr_valid; Bit32u paddr; unsigned ilen; if (range.to == EMPTY_ARG) { // should set to cs:eip. FIXME BX_INFO(("Error: type 'disassemble ADDR' or 'disassemble ADDR:ADDR'")); return; } do { BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(range.from, &paddr, &paddr_valid); if (paddr_valid) { BX_MEM(0)->dbg_fetch_mem(paddr, 16, bx_disasm_ibuf); ilen = bx_disassemble.disasm(bx_debugger.disassemble_size==32, bx_disasm_ibuf, bx_disasm_tbuf); fprintf(stderr, "%08x: ", (unsigned) range.from); for (unsigned j=0; j> biti) & 0x01; fputc(digit + '0', stderr); if(biti==0) break; if((biti%4) == 0) fputc(' ',stderr); } fprintf(stderr,"\n"); } void bx_dbg_v2l_command(unsigned seg_no, Bit32u offset) { #if BX_NUM_SIMULATORS > 1 fprintf(stderr, "Error: v2l not supported for nsim > 1\n" #else bx_dbg_sreg_t sreg; Bit32u laddr; if (seg_no > 5) { fprintf(stderr, "Error: seg_no out of bounds\n"); return; } BX_CPU(dbg_cpu)->dbg_get_sreg(&sreg, seg_no); if (!sreg.valid) { fprintf(stderr, "Error: segment valid bit cleared\n"); return; } laddr = (sreg.des_l>>16) | ((sreg.des_h<<16)&0x00ff0000) | (sreg.des_h & 0xff000000); laddr += offset; fprintf(stderr, "laddr: 0x%x (%u)\n", (unsigned) laddr, (unsigned) laddr); #endif } void bx_dbg_instrument_command(char *comm) { #if BX_INSTRUMENTATION if ( !strcmp(comm, "start") ) { BX_CPU(0)->instr_start(); } else if ( !strcmp(comm, "stop") ) { BX_CPU(0)->instr_stop(); } else if ( !strcmp(comm, "reset") ) { BX_CPU(0)->instr_reset(); } else if ( !strcmp(comm, "print") ) { BX_CPU(0)->instr_print(); } else { fprintf(stderr, "Error: command instrument %s not implemented.\n", comm); bx_dbg_exit(1); } #else UNUSED(comm); fprintf(stderr, "Error: instrumentation not enabled.\n"); #endif } void bx_dbg_loader_command(char *path_quoted) { size_t len; // skip beginning double quote if (path_quoted[0] == '"') path_quoted++; // null out ending quote len = strlen(path_quoted); if (path_quoted[len - 1] == '"') path_quoted[len - 1] = '\0'; #if BX_USE_LOADER { bx_loader_misc_t loader_misc; BX_CPU(0)->loader(path_quoted, &loader_misc); #if 0 fprintf(stderr, "dr0: 0x%08x\n", loader_misc.dr0); fprintf(stderr, "dr1: 0x%08x\n", loader_misc.dr1); fprintf(stderr, "dr2: 0x%08x\n", loader_misc.dr2); fprintf(stderr, "dr3: 0x%08x\n", loader_misc.dr3); fprintf(stderr, "dr6: 0x%08x\n", loader_misc.dr6); fprintf(stderr, "dr7: 0x%08x\n", loader_misc.dr7); #endif bx_cpu.dr0 = loader_misc.dr0; bx_cpu.dr1 = loader_misc.dr1; bx_cpu.dr2 = loader_misc.dr2; bx_cpu.dr3 = loader_misc.dr3; bx_cpu.dr7 = loader_misc.dr7; } #else fprintf(stderr, "Error: loader not implemented.\n"); #endif } void bx_dbg_doit_command(unsigned n) { // generic command to add temporary hacks to // for debugging purposes UNUSED(n); bx_dbg.interrupts = n; bx_dbg.exceptions = n; } void bx_dbg_crc_command(Bit32u addr1, Bit32u addr2) { Bit32u crc1, crc2; if (addr1 >= addr2) { fprintf(stderr, "Error: crc: invalid range.\n"); return; } if (!BX_MEM(0)->dbg_crc32(crc32, addr1, addr2, &crc1)) { fprintf(stderr, "sim0: could not CRC memory\n"); return; } #if BX_NUM_SIMULATORS == 1 fprintf(stderr, "0x%lx\n", crc1); #else if (!BX_MEM(1)->dbg_crc32(crc32, addr1, addr2, &crc2)) { fprintf(stderr, "sim1: could not CRC memory\n"); return; } if (crc1 == crc2) { fprintf(stderr, "CRC same: 0x%x\n", (unsigned) crc1); } else { fprintf(stderr, "CRC different: sim0=0x%x, sim1=0x%x\n", (unsigned) crc1, (unsigned) crc2); } #endif } void bx_dbg_info_dirty_command(void) { unsigned char *page_tbl = BX_MEM(0)->dbg_dirty_pages; unsigned page_tbl_size = BX_MEM(0)->dbg_count_dirty_pages (); for (unsigned i=0; i> 16) & 0xffff) | ((hi << 16) & 0xff0000) | (hi & 0xff000000); int limit = (lo & 0xffff); int segment = (lo >> 16) & 0xffff; int offset = (lo & 0xffff) | (hi & 0xffff0000); int type = (hi >> 8) & 0x0f; int dpl = (hi >> 13) & 0x03; int s = (hi >> 12) & 0x01; int present = (hi >> 15) & 0x01; int avl = (hi >> 20) & 0x01; int d_b = (hi >> 22) & 0x01; int g = (hi >> 23) & 0x01; int base_is_jump_addr; #if 0 if (s) { // either a code or a data segment. bit 11 (type file MSB) then says // 0=data segment, 1=code seg if (type&8) { fprintf (fp, "Segment type: Code, %s%s%s\n", (type&2)? "Execute/Read" : "Execute-Only", (type&4)? ", Conforming" : "", (type&1)? ", Accessed" : ""); fprintf (fp, "D flag=%d (use %d-bit addresses, %d-bit or 8-bit operands)\n", d_b, d_b? 32 : 16); } else { fprintf (fp, "Segment type: Data, %s%s%s\n", (type&2)? "Read/Write" : "Read-Only", (type&4)? ", Expand-down" : "", (type&1)? ", Accessed" : ""); } } else { // types from IA32-devel-guide-3, page 3-15. static char *type_names[16] = { "Reserved", "16-Bit TSS (available)", "LDT", "16-Bit TSS (Busy)", "16-Bit Call Gate", "Task Gate", "16-Bit Interrupt Gate", "16-Bit Trap Gate", "Reserved", "32-Bit TSS (Available)", "Reserved", "32-Bit TSS (Busy)", "32-Bit Call Gate", "Reserved", "32-Bit Interrupt Gate", "32-Bit Trap Gate" }; // some kind of gate? fprintf (fp, "System segment, type=0x%x=%s\n", type, type_names[type]); base_is_jump_addr = 1; // for call gates, print segment:offset and parameter count p.40-15 // for task gate, only present,dpl,TSS segment selector exist. p.5-13 // for interrupt gate, segment:offset,p,dpl // for trap gate, segment:offset,p,dpl } fprintf (fp, "DPL=descriptor privilege level=%d\n", dpl); if (base_is_jump_addr) { fprintf (fp, "target address=%04x:%08x\n", segment, offset); } else { fprintf (fp, "base address=%p\n", base); fprintf (fp, "G=granularity=%d\n", g); fprintf (fp, "limit=0x%x %s (see G)\n", limit, g?"4K-byte units" : "bytes"); fprintf (fp, "AVL=available to OS=%d\n", avl); } fprintf (fp, "P=present=%d\n", present); #endif /* brief output */ // 32-bit trap gate, target=0010:c0108ec4, DPL=0, present=1 // code segment, base=0000:00cfffff, length=0xffff if (s) { // either a code or a data segment. bit 11 (type file MSB) then says // 0=data segment, 1=code seg if (type&8) { fprintf (fp, "Code segment, linearaddr=%08x, len=%04x %s, %s%s%s, %d-bit addrs\n", base, limit, g ? "* 4Kbytes" : "bytes", (type&2)? "Execute/Read" : "Execute-Only", (type&4)? ", Conforming" : "", (type&1)? ", Accessed" : "", d_b? 32 : 16); } else { fprintf (fp, "Data segment, linearaddr=%08x, len=%04x %s, %s%s%s\n", base, limit, g ? "* 4Kbytes" : "bytes", (type&2)? "Read/Write" : "Read-Only", (type&4)? ", Expand-down" : "", (type&1)? ", Accessed" : ""); } } else { // types from IA32-devel-guide-3, page 3-15. static char *undef = "???"; static char *type_names[16] = { undef, "16-Bit TSS (available)", "LDT", "16-Bit TSS (Busy)", "16-Bit Call Gate", "Task Gate", "16-Bit Interrupt Gate", "16-Bit Trap Gate", undef, "32-Bit TSS (Available)", undef, "32-Bit TSS (Busy)", "32-Bit Call Gate", undef, "32-Bit Interrupt Gate", "32-Bit Trap Gate" }; fprintf (fp, "%s ", type_names[type]); // only print more if type is valid if (type_names[type] == undef) { fprintf (fp, "descriptor hi=%08x, lo=%08x", hi, lo); } else { fprintf (fp, "target=%04x:%08x, DPL=%d", segment, offset, dpl); // for call gates, print segment:offset and parameter count p.4-15 // for task gate, only present,dpl,TSS segment selector exist. p.5-13 // for interrupt gate, segment:offset,p,dpl // for trap gate, segment:offset,p,dpl } fprintf (fp, "\n"); } } void bx_dbg_info_idt_command(bx_num_range range) { bx_dbg_cpu_t cpu; BX_CPU(0)->dbg_get_cpu(&cpu); int n, print_table = 0; if (range.to == EMPTY_ARG) { // show all entries range.from = 0; range.to = (cpu.idtr.limit) / 8; print_table = 1; } if (print_table) fprintf (stderr, "Interrupt Descriptor Table (0x%08x):\n", cpu.idtr.base); for (n = range.from; n<=range.to; n++) { Bit32u paddr, paddr_valid; BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(cpu.idtr.base + 8*n, &paddr, &paddr_valid); if (!paddr_valid) { fprintf (stderr, "error: IDTR+8*%d points to invalid linear address %p\n", n, cpu.idtr.base); return; } // read 8-byte entry from IDT unsigned char entry[8]; BX_MEM(0)->dbg_fetch_mem (paddr, 8, entry); fprintf (stderr, "IDT[0x%02x]=", n); bx_dbg_print_descriptor (stderr, entry, 0); } if (print_table) fprintf (stderr, "You can list individual entries with 'info idt NUM' or groups with 'info idt NUM:NUM'\n"); } void bx_dbg_info_gdt_command(bx_num_range range) { bx_dbg_cpu_t cpu; BX_CPU(0)->dbg_get_cpu(&cpu); int n, print_table = 0; if (range.to == EMPTY_ARG) { // show all entries range.from = 0; range.to = (cpu.gdtr.limit) / 8; print_table = 1; } if (print_table) fprintf (stderr, "Global Descriptor Table (0x%08x):\n", cpu.gdtr.base); for (n = range.from; n<=range.to; n++) { Bit32u paddr, paddr_valid; BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(cpu.gdtr.base + 8*n, &paddr, &paddr_valid); if (!paddr_valid) { fprintf (stderr, "error: GDTR+8*%d points to invalid linear address %p\n", n, cpu.gdtr.base); return; } unsigned char entry[8]; // read 8-byte entry from GDT BX_MEM(0)->dbg_fetch_mem (paddr, 8, entry); fprintf (stderr, "GDT[0x%02x]=", n); bx_dbg_print_descriptor (stderr, entry, 0); } if (print_table) fprintf (stderr, "You can list individual interrupts with 'info gdt NUM'.\n"); } void bx_dbg_info_ldt_command(bx_num_range n) { bx_dbg_cpu_t cpu; BX_CPU(0)->dbg_get_cpu(&cpu); fprintf (stderr, "Local Descriptor Table output not implemented\n"); } void bx_dbg_info_tss_command(bx_num_range n) { bx_dbg_cpu_t cpu; BX_CPU(0)->dbg_get_cpu(&cpu); fprintf (stderr, "TSS output not implemented\n"); } bx_num_range make_num_range (Bit64s from, Bit64s to) { bx_num_range x; x.from = from; x.to = to; return x; } void bx_dbg_info_control_regs_command(void) { bx_dbg_cpu_t cpu; BX_CPU(0)->dbg_get_cpu(&cpu); int cr0 = cpu.cr0; int cr2 = cpu.cr2; int cr3 = cpu.cr3; int cr4 = cpu.cr4; fprintf (stderr, "CR0=0x%08x\n", cr0); fprintf (stderr, " PG=paging=%d\n", (cr0>>31) & 1); fprintf (stderr, " CD=cache disable=%d\n", (cr0>>30) & 1); fprintf (stderr, " NW=not write through=%d\n", (cr0>>29) & 1); fprintf (stderr, " AM=alignment mask=%d\n", (cr0>>18) & 1); fprintf (stderr, " WP=write protect=%d\n", (cr0>>16) & 1); fprintf (stderr, " NE=numeric error=%d\n", (cr0>>5) & 1); fprintf (stderr, " ET=extension type=%d\n", (cr0>>4) & 1); fprintf (stderr, " TS=task switched=%d\n", (cr0>>3) & 1); fprintf (stderr, " EM=FPU emulation=%d\n", (cr0>>2) & 1); fprintf (stderr, " MP=monitor coprocessor=%d\n", (cr0>>1) & 1); fprintf (stderr, " PE=protection enable=%d\n", (cr0>>0) & 1); fprintf (stderr, "CR2=page fault linear address=0x%08x\n", cr2); fprintf (stderr, "CR3=0x%08x\n", cr3); fprintf (stderr, " PCD=page-level cache disable=%d\n", (cr3>>4) & 1); fprintf (stderr, " PWT=page-level writes transparent=%d\n", (cr3>>3) & 1); fprintf (stderr, "CR4=0x%08x\n", cr4); fprintf (stderr, " VME=virtual-8086 mode extensions=%d\n", (cr4>>0) & 1); fprintf (stderr, " PVI=protected-mode virtual interrupts=%d\n", (cr4>>1) & 1); fprintf (stderr, " TSD=time stamp disable=%d\n", (cr4>>2) & 1); fprintf (stderr, " DE=debugging extensions=%d\n", (cr4>>3) & 1); fprintf (stderr, " PSE=page size extensions=%d\n", (cr4>>4) & 1); fprintf (stderr, " PAE=physical address extension=%d\n", (cr4>>5) & 1); fprintf (stderr, " MCE=machine check enable=%d\n", (cr4>>6) & 1); fprintf (stderr, " PGE=page global enable=%d\n", (cr4>>7) & 1); fprintf (stderr, " PCE=performance-monitor counter enable=%d\n", (cr4>>8) & 1); fprintf (stderr, " OXFXSR=OS support for FXSAVE/FXRSTOR=%d\n", (cr4>>9) & 1); fprintf (stderr, " OSXMMEXCPT=OS support for unmasked SIMD FP exceptions=%d\n", (cr4>>10) & 1); } // // Reports from various events // void bx_dbg_iac_report(unsigned vector, unsigned irq) { #if BX_NUM_SIMULATORS > 1 unsigned tail, master; #endif if (doit) fprintf(stderr, "iac report: vector=%u\n", vector); if (bx_guard.report.irq) { fprintf(stderr, "event icount=%u IRQ irq=%u vec=%x\n", (unsigned) BX_CPU(dbg_cpu)->guard_found.icount, irq, vector); } #if BX_NUM_SIMULATORS > 1 if (bx_debugger.master_slave_mode == BX_DBG_SLAVE_MODE ) { fprintf(stderr, "Error: iac_report: in slave mode.\n"); bx_dbg_exit(1); } // Master simulator mode if (bx_debugger.async_journal.size >= BX_DBG_ASYNC_JOURNAL_SIZE) { fprintf(stderr, "Error: iac: async journal full.\n"); bx_dbg_exit(1); } if (bx_debugger.async_journal.size == 0) { // start off point head & tail at same element bx_debugger.async_journal.head = 0; tail = bx_debugger.async_journal.tail = 0; } else { tail = bx_debugger.async_journal.tail + 1; } if (tail >= BX_DBG_ASYNC_JOURNAL_SIZE) { fprintf(stderr, "Error: iac_report: journal wrapped.\n"); bx_dbg_exit(0); } master = bx_debugger.master; bx_debugger.async_journal.element[tail].what = BX_DBG_ASYNC_JOURNAL_IAC; bx_debugger.async_journal.element[tail].icount = bx_guard_found[master].icount; bx_debugger.async_journal.element[tail].u.iac.val = vector; if (bx_debugger.async_journal.size) bx_debugger.async_journal.tail++; bx_debugger.async_journal.size++; #endif } void bx_dbg_a20_report(unsigned val) { if (bx_guard.report.a20) { fprintf(stderr, "event icount=%u A20 val=%u\n", (unsigned) BX_CPU(dbg_cpu)->guard_found.icount, val); } } #if BX_NUM_SIMULATORS > 1 void bx_dbg_journal_a20_event(unsigned val) { unsigned tail, master; if (bx_debugger.master_slave_mode == BX_DBG_SLAVE_MODE ) { fprintf(stderr, "Error: a20_report: in slave mode.\n"); bx_dbg_exit(1); } // Master simulator mode if (bx_debugger.async_journal.size >= BX_DBG_ASYNC_JOURNAL_SIZE) { fprintf(stderr, "Error: async journal full.\n"); bx_dbg_exit(1); } if (bx_debugger.async_journal.size == 0) { // start off point head & tail at same element bx_debugger.async_journal.head = 0; tail = bx_debugger.async_journal.tail = 0; } else { tail = bx_debugger.async_journal.tail + 1; } if (tail >= BX_DBG_ASYNC_JOURNAL_SIZE) { fprintf(stderr, "Error: a20_report: journal wrapped.\n"); bx_dbg_exit(0); } master = bx_debugger.master; bx_debugger.async_journal.element[tail].what = BX_DBG_ASYNC_JOURNAL_A20; bx_debugger.async_journal.element[tail].icount = bx_guard_found[master].icount; bx_debugger.async_journal.element[tail].u.a20.val = val; if (bx_debugger.async_journal.size) bx_debugger.async_journal.tail++; bx_debugger.async_journal.size++; } #endif void bx_dbg_io_report(Bit32u addr, unsigned size, unsigned op, Bit32u val) { if (bx_guard.report.io) { fprintf(stderr, "event icount=%u IO addr=0x%x size=%u op=%s val=0x%x\n", (unsigned) BX_CPU(dbg_cpu)->guard_found.icount, (unsigned) addr, size, (op==BX_READ) ? "read" : "write", (unsigned) val); } // nothing else to do. bx_dbg_inp() and bx_dbg_outp() do the journaling. } void bx_dbg_ucmem_report(Bit32u addr, unsigned size, unsigned op, Bit32u val) { if (bx_guard.report.ucmem) { fprintf(stderr, "event icount=%u UCmem addr=0x%x size=%u op=%s val=0x%x\n", (unsigned) BX_CPU(dbg_cpu)->guard_found.icount, (unsigned) addr, size, (op==BX_READ) ? "read" : "write", (unsigned) val); } // nothing else to do. bx_dbg_ucmem_read() and bx_dbg_ucmem_write() // do the journaling. } void bx_dbg_dma_report(Bit32u addr, unsigned len, unsigned what, Bit32u val) { if (bx_dbg_batch_dma.this_many == 0) { fprintf(stderr, "%s: DMA batch this_many=0.\n", argv0); bx_dbg_exit(1); } // if Q is full, post events (and flush) if (bx_dbg_batch_dma.Qsize >= bx_dbg_batch_dma.this_many) { fprintf(stderr, "%s: DMA batch Q was not flushed.\n", argv0); bx_dbg_exit(1); } // if Q already has MAX elements in it if (bx_dbg_batch_dma.Qsize >= BX_BATCH_DMA_BUFSIZE) { fprintf(stderr, "%s: DMA batch buffer overrun.\n", argv0); bx_dbg_exit(1); } bx_dbg_batch_dma.Qsize++; bx_dbg_batch_dma.Q[bx_dbg_batch_dma.Qsize-1].addr = addr; bx_dbg_batch_dma.Q[bx_dbg_batch_dma.Qsize-1].len = len; bx_dbg_batch_dma.Q[bx_dbg_batch_dma.Qsize-1].what = what; bx_dbg_batch_dma.Q[bx_dbg_batch_dma.Qsize-1].val = val; bx_dbg_batch_dma.Q[bx_dbg_batch_dma.Qsize-1].icount = BX_CPU(dbg_cpu)->guard_found.icount; // if Q is full, post events (and flush) if (bx_dbg_batch_dma.Qsize >= bx_dbg_batch_dma.this_many) bx_dbg_post_dma_reports(); } void bx_dbg_post_dma_reports(void) { unsigned i; unsigned addr, len, what, val; unsigned last_addr, last_len, last_what; unsigned print_header; unsigned first_iteration; if (bx_guard.report.dma) { if (bx_dbg_batch_dma.Qsize == 0) return; // nothing batched to print // compress output so all contiguous DMA ops of the same type and size // are printed on the same line last_addr = bx_dbg_batch_dma.Q[0].addr; last_len = bx_dbg_batch_dma.Q[0].len; last_what = bx_dbg_batch_dma.Q[0].what; first_iteration = 1; for (i=0; i= 2) Bit8u bx_dbg_ucmem_read(Bit32u addr) { Bit8u value; unsigned head, tail; if ( bx_debugger.master_slave_mode == BX_DBG_MASTER_MODE ) { if (!bx_debugger.fast_forward_mode) { if (bx_debugger.UCmem_journal.size >= BX_DBG_UCMEM_JOURNAL_SIZE) { fprintf(stderr, "dbg_ucmem_read: journal full.\n"); bx_dbg_exit(0); } if (bx_debugger.UCmem_journal.size == 0) { // start off point head & tail at same element bx_debugger.UCmem_journal.head = 0; tail = bx_debugger.UCmem_journal.tail = 0; } else { tail = bx_debugger.UCmem_journal.tail + 1; } if (tail >= BX_DBG_UCMEM_JOURNAL_SIZE) { fprintf(stderr, "dbg_ucmem_read: journal wrapped.\n"); bx_dbg_exit(0); } value = bx_devices.vga->mem_read(addr); bx_dbg_ucmem_report(addr, 1, BX_READ, value); bx_debugger.UCmem_journal.element[tail].op = BX_READ; bx_debugger.UCmem_journal.element[tail].len = 1; bx_debugger.UCmem_journal.element[tail].addr = addr; bx_debugger.UCmem_journal.element[tail].value = value; if (bx_debugger.UCmem_journal.size) bx_debugger.UCmem_journal.tail++; bx_debugger.UCmem_journal.size++; if (doit) fprintf(stderr, "MASTER UCR: head:%u tail%u size:%u\n", bx_debugger.UCmem_journal.head, bx_debugger.UCmem_journal.tail, bx_debugger.UCmem_journal.size); return(value); } else { value = bx_devices.vga->mem_read(addr); return(value); } } else { if (bx_debugger.UCmem_journal.size == 0) { fprintf(stderr, "Error: ucmem_read: journal empty.\n"); return(0xff); } head = bx_debugger.UCmem_journal.head; value = bx_debugger.UCmem_journal.element[head].value; if ((bx_debugger.UCmem_journal.element[head].op != BX_READ) || (bx_debugger.UCmem_journal.element[head].len != 1) || (bx_debugger.UCmem_journal.element[head].addr != addr)) { fprintf(stderr, "Error: ucmem_read: out of sync with journal.\n"); fprintf(stderr, "Error: master: op=%1s len=%u addr=0x%x val=0x%x\n", (bx_debugger.UCmem_journal.element[head].op==BX_READ) ? "W" : "R", (unsigned) bx_debugger.UCmem_journal.element[head].len, (unsigned) bx_debugger.UCmem_journal.element[head].addr, (unsigned) bx_debugger.UCmem_journal.element[head].value); fprintf(stderr, "Error: slave: op=W len=%u addr=0x%x val=0x%x\n", (unsigned) 1, (unsigned) addr, (unsigned) value); return(0xff); } // slave UCmem op in sync with journaled master op, delete this entry bx_debugger.UCmem_journal.head++; bx_debugger.UCmem_journal.size--; return(value); } } void bx_dbg_ucmem_write(Bit32u addr, Bit8u value) { unsigned tail, head; if ( bx_debugger.master_slave_mode == BX_DBG_MASTER_MODE ) { if (!bx_debugger.fast_forward_mode) { if (bx_debugger.UCmem_journal.size >= BX_DBG_UCMEM_JOURNAL_SIZE) { fprintf(stderr, "dbg_ucmem_write: journal full.\n"); bx_dbg_exit(0); } if (bx_debugger.UCmem_journal.size == 0) { // start off point head & tail at same element bx_debugger.UCmem_journal.head = 0; tail = bx_debugger.UCmem_journal.tail = 0; } else { tail = bx_debugger.UCmem_journal.tail + 1; } if (tail >= BX_DBG_UCMEM_JOURNAL_SIZE) { fprintf(stderr, "dbg_ucmem_write: journal wrapped.\n"); bx_dbg_exit(0); } bx_debugger.UCmem_journal.element[tail].op = BX_WRITE; bx_debugger.UCmem_journal.element[tail].len = 1; bx_debugger.UCmem_journal.element[tail].addr = addr; bx_debugger.UCmem_journal.element[tail].value = value; if (bx_debugger.UCmem_journal.size) bx_debugger.UCmem_journal.tail++; bx_debugger.UCmem_journal.size++; bx_devices.vga->mem_write(addr, value); bx_dbg_ucmem_report(addr, 1, BX_WRITE, value); } else { bx_devices.vga->mem_write(addr, value); } } else { if (bx_debugger.UCmem_journal.size == 0) { fprintf(stderr, "Error: ucmem_write: journal empty.\n"); return; } head = bx_debugger.UCmem_journal.head; if ((bx_debugger.UCmem_journal.element[head].op != BX_WRITE) || (bx_debugger.UCmem_journal.element[head].len != 1) || (bx_debugger.UCmem_journal.element[head].addr != addr) || (bx_debugger.UCmem_journal.element[head].value != value) ) { fprintf(stderr, "Error: ucmem_write: out of sync with journal.\n"); fprintf(stderr, "Error: master: op=%1s len=%u addr=0x%x val=0x%x\n", (bx_debugger.UCmem_journal.element[head].op==BX_WRITE) ? "W" : "R", (unsigned) bx_debugger.UCmem_journal.element[head].len, (unsigned) bx_debugger.UCmem_journal.element[head].addr, (unsigned) bx_debugger.UCmem_journal.element[head].value); fprintf(stderr, "Error: slave: op=W len=%u addr=0x%x val=0x%x\n", (unsigned) 1, (unsigned) addr, (unsigned) value); return; } // slave UCmem op in sync with journaled master op, delete this entry bx_debugger.UCmem_journal.head++; bx_debugger.UCmem_journal.size--; } } void bx_dbg_async_pin_request(unsigned what, Boolean val) { // Request from IO devices for change in pin external to CPU. // This is pended until CPU ack's with bx_dbg_async_pin_ack(). if (bx_debugger.master_slave_mode != BX_DBG_MASTER_MODE) { fprintf(stderr, "Error: dbg_async_pin_request not in master mode.\n"); bx_dbg_exit(1); } switch (what) { case BX_DBG_ASYNC_PENDING_A20: // Q pending status bx_guard.async_changes_pending.which |= BX_DBG_ASYNC_PENDING_A20; bx_guard.async_changes_pending.a20 = val; return; break; case BX_DBG_ASYNC_PENDING_RESET: case BX_DBG_ASYNC_PENDING_NMI: default: fprintf(stderr, "Error: set_async_pin: unhandled case.\n"); bx_dbg_exit(1); } } void bx_dbg_async_pin_ack(unsigned what, Boolean val) { // Acknowledgement from master simulator for pending change in pin // external to CPU. if (bx_debugger.master_slave_mode != BX_DBG_MASTER_MODE) { fprintf(stderr, "Error: dbg_async_pin_ack: not master mode.\n"); bx_dbg_exit(1); } switch (what) { case BX_DBG_ASYNC_PENDING_A20: // get rid of pending status bx_guard.async_changes_pending.which &= ~BX_DBG_ASYNC_PENDING_A20; // notify pc_system of change bx_pc_system.set_enable_a20(val); if (BX_CPU(bx_debugger.master)->set_A20) BX_CPU(bx_debugger.master)->set_A20(val); bx_dbg_journal_a20_event(val); return; break; case BX_DBG_ASYNC_PENDING_RESET: case BX_DBG_ASYNC_PENDING_NMI: default: fprintf(stderr, "Error: set_async_pin: unhandled case.\n"); bx_dbg_exit(1); } } Bit32u bx_dbg_inp(Bit16u addr, unsigned len) { Bit32u value; unsigned tail, head; if ( bx_debugger.master_slave_mode == BX_DBG_MASTER_MODE ) { if (!bx_debugger.fast_forward_mode) { if (bx_debugger.IO_journal.size >= BX_DBG_IO_JOURNAL_SIZE) { fprintf(stderr, "dbg_inp: journal full.\n"); bx_dbg_exit(0); } if (bx_debugger.IO_journal.size == 0) { // start off point head & tail at same element bx_debugger.IO_journal.head = 0; tail = bx_debugger.IO_journal.tail = 0; } else { tail = bx_debugger.IO_journal.tail + 1; } if (tail >= BX_DBG_IO_JOURNAL_SIZE) { fprintf(stderr, "dbg_inp: journal wrapped.\n"); bx_dbg_exit(0); } value = bx_pc_system.inp(addr, len); bx_debugger.IO_journal.element[tail].op = BX_READ; bx_debugger.IO_journal.element[tail].len = (Bit8u) len; bx_debugger.IO_journal.element[tail].addr = addr; bx_debugger.IO_journal.element[tail].value = value; if (bx_debugger.IO_journal.size) bx_debugger.IO_journal.tail++; bx_debugger.IO_journal.size++; //fprintf(stderr, "MASTER IN: head:%u tail%u size:%u\n", // bx_debugger.IO_journal.head, // bx_debugger.IO_journal.tail, // bx_debugger.IO_journal.size); return(value); } else { value = bx_pc_system.inp(addr, len); return(value); } } else { if (bx_debugger.IO_journal.size == 0) { fprintf(stderr, "Error: dbg_inp: journal empty.\n"); return(0xffffffff); } head = bx_debugger.IO_journal.head; value = bx_debugger.IO_journal.element[head].value; if ((bx_debugger.IO_journal.element[head].op != BX_READ) || (bx_debugger.IO_journal.element[head].len != len) || (bx_debugger.IO_journal.element[head].addr != addr) ) { fprintf(stderr, "Error: dbg_inp: out of sync with journal.\n"); fprintf(stderr, "Error: master: op=%3s len=%u addr=0x%x\n", (bx_debugger.IO_journal.element[head].op==BX_WRITE) ? "OUT" : "IN", (unsigned) bx_debugger.IO_journal.element[head].len, (unsigned) bx_debugger.IO_journal.element[head].addr); fprintf(stderr, "Error: slave: op=OUT len=%u addr=0x%x\n", (unsigned) len, (unsigned) addr); return(0xffffffff); } // slave IO op in sync with journaled master op, delete this entry bx_debugger.IO_journal.head++; bx_debugger.IO_journal.size--; // fprintf(stderr, "SLAVE IN: head:%u tail%u size:%u\n", // bx_debugger.IO_journal.head, // bx_debugger.IO_journal.tail, // bx_debugger.IO_journal.size); return(value); } } void bx_dbg_outp(Bit16u addr, Bit32u value, unsigned len) { unsigned tail, head; if ( bx_debugger.master_slave_mode == BX_DBG_MASTER_MODE ) { if (!bx_debugger.fast_forward_mode) { if (bx_debugger.IO_journal.size >= BX_DBG_IO_JOURNAL_SIZE) { fprintf(stderr, "dbg_outp: IO journal full.\n"); bx_dbg_exit(0); } if (bx_debugger.IO_journal.size == 0) { // start off point head & tail at same element bx_debugger.IO_journal.head = 0; tail = bx_debugger.IO_journal.tail = 0; } else { tail = bx_debugger.IO_journal.tail + 1; } if (tail >= BX_DBG_IO_JOURNAL_SIZE) { fprintf(stderr, "dbg_outp: IO journal wrapped.\n"); bx_dbg_exit(0); } bx_debugger.IO_journal.element[tail].op = BX_WRITE; bx_debugger.IO_journal.element[tail].len = (Bit8u) len; bx_debugger.IO_journal.element[tail].addr = addr; bx_debugger.IO_journal.element[tail].value = value; if (bx_debugger.IO_journal.size) bx_debugger.IO_journal.tail++; bx_debugger.IO_journal.size++; bx_pc_system.outp(addr, value, len); if (doit) fprintf(stderr, "master: IO journal size now %u\n", bx_debugger.IO_journal.size); } else { bx_pc_system.outp(addr, value, len); } } else { if (bx_debugger.IO_journal.size == 0) { fprintf(stderr, "Error: dbg_outp: journal empty.\n"); return; } head = bx_debugger.IO_journal.head; if ((bx_debugger.IO_journal.element[head].op != BX_WRITE) || (bx_debugger.IO_journal.element[head].len != len) || (bx_debugger.IO_journal.element[head].addr != addr) || (bx_debugger.IO_journal.element[head].value != value) ) { fprintf(stderr, "Error: dbg_outp: out of sync with journal.\n"); fprintf(stderr, "Error: master: op=%3s len=%u addr=0x%x val=0x%x\n", (bx_debugger.IO_journal.element[head].op==BX_WRITE) ? "OUT" : "IN", (unsigned) bx_debugger.IO_journal.element[head].len, (unsigned) bx_debugger.IO_journal.element[head].addr, (unsigned) bx_debugger.IO_journal.element[head].value); fprintf(stderr, "Error: slave: op=OUT len=%u addr=0x%x val=0x%x\n", (unsigned) len, (unsigned) addr, (unsigned) value); return; } // slave IO op in sync with journaled master op, delete this entry bx_debugger.IO_journal.head++; bx_debugger.IO_journal.size--; if (doit) fprintf(stderr, "slave: IO journal size now %u\n", bx_debugger.IO_journal.size); } } void bx_dbg_raise_HLDA(void) { fprintf(stderr, "dbg_HLDA called\n"); bx_dbg_exit(0); } Bit8u bx_dbg_IAC(void) { // Convience routine. bochs skips this, and calls the PIC code // directly. This is for other simulators to interface to the // the PIC code. unsigned iac; iac = bx_devices.pic->IAC(); return(iac); } void bx_dbg_set_INTR(Boolean b) { if ( bx_debugger.master_slave_mode == BX_DBG_SLAVE_MODE ) { fprintf(stderr, "Error: set_INTR in slave mode.\n"); bx_dbg_exit(1); } bx_pc_system.INTR = b; BX_CPU(bx_debugger.master)->set_INTR(b); } #endif // #if (BX_NUM_SIMULATORS >= 2) // BW added. return non zero to cause a stop #if BX_DEBUGGER static int symbol_level; int bx_dbg_symbolic_output(void) { /* modes & address spaces */ if(BX_CPU(dbg_cpu)->cr0.pe != last_pe) { fprintf(stderr,"%10lld: Switched %s protected mode\n", bx_pc_system.time_ticks(), last_pe ? "from" : "to"); last_pe = !last_pe; } if(last_vm != BX_CPU(dbg_cpu)->eflags.vm) { fprintf(stderr,"%10lld: %s V86 mode\n", bx_pc_system.time_ticks(), last_vm ? "Exited" : "Entered"); last_vm = !last_vm; } if(last_cr3 != BX_CPU(dbg_cpu)->cr3) fprintf(stderr,"\n%10lld: Address space switched since last trigger. CR3: 0x%08x\n", bx_pc_system.time_ticks(), BX_CPU(dbg_cpu)->cr3); /* interrupts */ if (dbg_show_mask & 0x40) { if(BX_CPU(dbg_cpu)->show_flag & 0x4) { fprintf(stderr,"%10lld: softint %04x:%08x %08x\n", bx_pc_system.time_ticks(), BX_CPU(dbg_cpu)->guard_found.cs, BX_CPU(dbg_cpu)->guard_found.eip, BX_CPU(dbg_cpu)->guard_found.laddr); } if((BX_CPU(dbg_cpu)->show_flag & 0x10) && !(BX_CPU(dbg_cpu)->show_flag & 0x4)) { fprintf(stderr,"\n%10lld: exception (not softint) %04x:%08x %08x\n", bx_pc_system.time_ticks(), BX_CPU(dbg_cpu)->guard_found.cs, BX_CPU(dbg_cpu)->guard_found.eip, BX_CPU(dbg_cpu)->guard_found.laddr); } if(BX_CPU(dbg_cpu)->show_flag & 0x8) { fprintf(stderr,"%10lld: iret %04x:%08x %08x (from %08x)\n\n", bx_pc_system.time_ticks(), BX_CPU(dbg_cpu)->guard_found.cs, BX_CPU(dbg_cpu)->guard_found.eip, BX_CPU(dbg_cpu)->guard_found.laddr, BX_CPU(dbg_cpu)->show_eip); } } /* calls */ if(BX_CPU(dbg_cpu)->show_flag & 0x1) { Bit32u phy = 0; Boolean valid; if (dbg_show_mask & 0x20) { BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(BX_CPU(dbg_cpu)->guard_found.laddr, &phy, &valid); fprintf(stderr,"%10lld:%*s call %04x:%08x 0x%08x (%08x) %s", bx_pc_system.time_ticks(), symbol_level+1," ", BX_CPU(dbg_cpu)->guard_found.cs, BX_CPU(dbg_cpu)->guard_found.eip, BX_CPU(dbg_cpu)->guard_found.laddr, phy, bx_dbg_symbolic_address(BX_CPU(dbg_cpu)->cr3, BX_CPU(dbg_cpu)->guard_found.eip, BX_CPU(dbg_cpu)->guard_found.laddr - BX_CPU(dbg_cpu)->guard_found.eip) ); if(!valid) fprintf(stderr," phys not valid"); fprintf(stderr,"\n"); } symbol_level++; if(symbol_level > 40) symbol_level = 10; } if (BX_CPU(dbg_cpu)->show_flag & 0x2) { symbol_level--; if(symbol_level < 0) symbol_level = 0; } BX_CPU(dbg_cpu)->show_flag = 0; last_cr3 = BX_CPU(dbg_cpu)->cr3; return 0; } #endif // BW added to dump page table static void dbg_lin2phys(BX_CPU_C *cpu, Bit32u laddress, Bit32u *phy, Boolean *valid, Bit32u *tlb_phy, Boolean *tlb_valid) { Bit32u lpf, ppf, poffset, TLB_index, paddress; Bit32u pde, pde_addr; Bit32u pte, pte_addr; *tlb_valid = 0; if (cpu->cr0.pg == 0) { *phy = laddress; *valid = 1; return; } lpf = laddress & 0xfffff000; // linear page frame poffset = laddress & 0x00000fff; // physical offset TLB_index = BX_TLB_INDEX_OF(lpf); // see if page is in the TLB first if (cpu->TLB.entry[TLB_index].lpf == lpf) { *tlb_phy = cpu->TLB.entry[TLB_index].ppf | poffset; *tlb_valid = 1; } // Get page dir entry pde_addr = (cpu->cr3 & 0xfffff000) | ((laddress & 0xffc00000) >> 20); BX_MEM(0)->read_physical(cpu, pde_addr, 4, &pde); if ( !(pde & 0x01) ) { // Page Directory Entry NOT present goto page_fault; } // Get page table entry pte_addr = (pde & 0xfffff000) | ((laddress & 0x003ff000) >> 10); BX_MEM(0)->read_physical(cpu, pte_addr, 4, &pte); if ( !(pte & 0x01) ) { // Page Table Entry NOT present goto page_fault; } ppf = pte & 0xfffff000; paddress = ppf | poffset; *phy = paddress; *valid = 1; return; page_fault: *phy = 0; *valid = 0; return; } static void dbg_dump_table(Boolean all) { Bit32u lina; Bit32u phy, tlb_phy; Boolean valid, tlb_valid; Bit32u start_lina, start_phy; // start of a valid translation interval if (BX_CPU(dbg_cpu)->cr0.pg == 0) { printf("paging off\n"); return; } printf("cr3: %08x \n", BX_CPU(dbg_cpu)->cr3); lina = 0; start_lina = 1; start_phy = 2; while(1) { dbg_lin2phys(BX_CPU(dbg_cpu), lina, &phy, &valid, &tlb_phy, &tlb_valid); if(valid) { if( (lina - start_lina != phy - start_phy) || tlb_valid) { if(all && (start_lina != 1)) printf("%08x - %08x: %8x - %8x\n", start_lina, lina - 0x1000, start_phy, start_phy + (lina-0x1000-start_lina)); start_lina = lina; start_phy = phy; } if(tlb_valid) { if(all && tlb_phy == phy) printf("%08x : %8x (%8x) in TLB\n", lina, phy, tlb_phy); if(tlb_phy != phy) printf("%08x : %8x (%8x) in TLB Phys differs!!!\n", lina, phy, tlb_phy); start_lina = 1; start_phy = 2; } } else { if(all && start_lina != 1) printf("%08x - %08x: %8x - %8x\n", start_lina, lina - 0x1000, start_phy, start_phy + (lina-0x1000-start_lina)); if(tlb_valid) { printf("%08x : (%8x) in TLB Table not valid!!!\n", lina, tlb_phy); } start_lina = 1; start_phy = 2; } if(lina == 0xfffff000) break; lina += 0x1000; } if(all & start_lina != 1) printf("%08x - %08x: %8x - %8x\n", start_lina, 0xfffff000, start_phy, start_phy + (0xfffff000-start_lina)); }