cec9135e9f
"bx_bool" which is always defined as Bit32u on all platforms. In Carbon specific code, Boolean is still used because the Carbon header files define it to unsigned char. - this fixes bug [ 623152 ] MacOSX: Triple Exception Booting win95. The bug was that some code in Bochs depends on Boolean to be a 32 bit value. (This should be fixed, but I don't know all the places where it needs to be fixed yet.) Because Carbon defined Boolean as an unsigned char, Bochs just followed along and used the unsigned char definition to avoid compile problems. This exposed the dependency on 32 bit Boolean on MacOS X only and led to major simulation problems, that could only be reproduced and debugged on that platform. - On the mailing list we debated whether to make all Booleans into "bool" or our own type. I chose bx_bool for several reasons. 1. Unlike C++'s bool, we can guarantee that bx_bool is the same size on all platforms, which makes it much less likely to have more platform-specific simulation differences in the future. (I spent hours on a borrowed MacOSX machine chasing bug 618388 before discovering that different sized Booleans were the problem, and I don't want to repeat that.) 2. We still have at least one dependency on 32 bit Booleans which must be fixed some time, but I don't want to risk introducing new bugs into the simulation just before the 2.0 release. Modified Files: bochs.h config.h.in gdbstub.cc logio.cc main.cc pc_system.cc pc_system.h plugin.cc plugin.h bios/rombios.c cpu/apic.cc cpu/arith16.cc cpu/arith32.cc cpu/arith64.cc cpu/arith8.cc cpu/cpu.cc cpu/cpu.h cpu/ctrl_xfer16.cc cpu/ctrl_xfer32.cc cpu/ctrl_xfer64.cc cpu/data_xfer16.cc cpu/data_xfer32.cc cpu/data_xfer64.cc cpu/debugstuff.cc cpu/exception.cc cpu/fetchdecode.cc cpu/flag_ctrl_pro.cc cpu/init.cc cpu/io_pro.cc cpu/lazy_flags.cc cpu/lazy_flags.h cpu/mult16.cc cpu/mult32.cc cpu/mult64.cc cpu/mult8.cc cpu/paging.cc cpu/proc_ctrl.cc cpu/segment_ctrl_pro.cc cpu/stack_pro.cc cpu/tasking.cc debug/dbg_main.cc debug/debug.h debug/sim2.cc disasm/dis_decode.cc disasm/disasm.h doc/docbook/Makefile docs-html/cosimulation.html fpu/wmFPUemu_glue.cc gui/amigaos.cc gui/beos.cc gui/carbon.cc gui/gui.cc gui/gui.h gui/keymap.cc gui/keymap.h gui/macintosh.cc gui/nogui.cc gui/rfb.cc gui/sdl.cc gui/siminterface.cc gui/siminterface.h gui/term.cc gui/win32.cc gui/wx.cc gui/wxmain.cc gui/wxmain.h gui/x.cc instrument/example0/instrument.cc instrument/example0/instrument.h instrument/example1/instrument.cc instrument/example1/instrument.h instrument/stubs/instrument.cc instrument/stubs/instrument.h iodev/cdrom.cc iodev/cdrom.h iodev/cdrom_osx.cc iodev/cmos.cc iodev/devices.cc iodev/dma.cc iodev/dma.h iodev/eth_arpback.cc iodev/eth_packetmaker.cc iodev/eth_packetmaker.h iodev/floppy.cc iodev/floppy.h iodev/guest2host.h iodev/harddrv.cc iodev/harddrv.h iodev/ioapic.cc iodev/ioapic.h iodev/iodebug.cc iodev/iodev.h iodev/keyboard.cc iodev/keyboard.h iodev/ne2k.h iodev/parallel.h iodev/pci.cc iodev/pci.h iodev/pic.h iodev/pit.cc iodev/pit.h iodev/pit_wrap.cc iodev/pit_wrap.h iodev/sb16.cc iodev/sb16.h iodev/serial.cc iodev/serial.h iodev/vga.cc iodev/vga.h memory/memory.h memory/misc_mem.cc
607 lines
26 KiB
HTML
607 lines
26 KiB
HTML
<HTML>
|
|
|
|
<HEAD>
|
|
<META NAME="copyright" CONTENT="Copyright 2001 by MandrakeSoft S.A.">
|
|
<META NAME="Author" CONTENT="Kevin Lawton">
|
|
<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1">
|
|
<TITLE>Cosimulating: Using Bochs debugger to drive 2 simulators</TITLE>
|
|
</HEAD>
|
|
|
|
<BODY TEXT="#000000" BGCOLOR="#ececec" LINK="#3333cc" VLINK="#666666">
|
|
|
|
<CENTER><H1><I>Welcome to the Bochs x86 PC Emulation Software Home Page!</I></H1></CENTER>
|
|
|
|
<CENTER><H1>Cosimulating: using Bochs debugger to drive 2 simulators</H1></CENTER>
|
|
|
|
<HR SIZE=5 NOSHADE>
|
|
<H2>What is Cosimulation?</H2>
|
|
|
|
<IMG SRC="undercon.gif" ALT="Under Construction">
|
|
I'll add a discussion of cosimulation here soon.
|
|
|
|
<HR SIZE=5 NOSHADE>
|
|
<H2>Cosimulation Programmatic Interface</H2>
|
|
<IMG SRC="undercon.gif" ALT="Under Construction">
|
|
This documentation is not yet complete, and this interface is
|
|
subject to change.
|
|
|
|
<HR>
|
|
<HR>
|
|
<H3>Callback Structure:</H3>
|
|
|
|
<P>Upon startup, the cosimulation controller (debugger) will initialize each
|
|
simulator (or emulator) by calling an init routine. There are 2
|
|
macro's in config.h (generated from config.h.in), which determine
|
|
the names of the initialization routines, one for each simulator.
|
|
These macro's are BX_SIM1_INIT and BX_SIM2_INIT. The first one
|
|
likely can be kept as is. You will need to edit the second
|
|
one, to be a routine in the second simulator which will receive
|
|
a few parameters, and fill in a callback structure. This is the
|
|
only routine in each simulator environment which is needed directly,
|
|
as the callback routines which are filled in, are used subsequently.
|
|
It should be of the type:
|
|
|
|
<PRE>
|
|
void some_name_here(bx_dbg_callback_t *, int argc, char *argv[]);
|
|
</PRE>
|
|
|
|
The <I>argc</I> and <I>argv</I> parameters are as you might expect.
|
|
They consist of any simulator specific command line options passed to
|
|
the bochs executable at program invocation time. Since there can be
|
|
more than one simulator, command line options are delimited by
|
|
<I>-sim1</I> and <I>-sim2</I> as in:
|
|
|
|
<PRE>
|
|
Usage: bochs [-rc path] [-sim1 ... ] [-sim2 ...]
|
|
</PRE>
|
|
|
|
The first parameter is the address of a callback structure, whose type
|
|
<I>bx_dbg_callback_t</I> is defined in debug/debug.h. Most fields
|
|
are function pointers, though some are not. Below, is a description
|
|
of each field in the callback structure.
|
|
|
|
<P>
|
|
<PRE>
|
|
typedef struct {
|
|
bx_bool (*setphymem)(Bit32u addr, unsigned len, Bit8u *buf);
|
|
bx_bool (*getphymem)(Bit32u addr, unsigned len, Bit8u *buf);
|
|
void (*xlate_linear2phy)(Bit32u linear, Bit32u *phy, bx_bool *valid);
|
|
bx_bool (*set_reg)(unsigned reg, Bit32u val);
|
|
Bit32u (*get_reg)(unsigned reg);
|
|
bx_bool (*set_cpu)(bx_dbg_cpu_t *cpu);
|
|
bx_bool (*get_cpu)(bx_dbg_cpu_t *cpu);
|
|
unsigned dirty_page_tbl_size;
|
|
unsigned char *dirty_page_tbl;
|
|
void (*atexit)(void);
|
|
unsigned (*query_pending)(void);
|
|
void (*execute)(void);
|
|
void (*take_irq)(void);
|
|
void (*take_dma)(void);
|
|
void (*reset_cpu)(unsigned source);
|
|
void (*init_mem)(int size_in_bytes);
|
|
void (*load_ROM)(const char *path, Bit32u romaddress);
|
|
|
|
void (*set_A20)(unsigned val);
|
|
void (*set_NMI)(unsigned val);
|
|
void (*set_RESET)(unsigned val);
|
|
void (*set_INTR)(unsigned val);
|
|
void (*force_interrupt)(unsigned vector);
|
|
|
|
#if BX_INSTRUMENTATION
|
|
void (*instr_start)(void);
|
|
void (*instr_stop)(void);
|
|
void (*instr_reset)(void);
|
|
void (*instr_print)(void);
|
|
#endif
|
|
#if BX_USE_LOADER
|
|
void (*loader)(char *path);
|
|
#endif
|
|
} bx_dbg_callback_t;
|
|
</PRE>
|
|
|
|
<P><B>bx_bool (*setphymem)(Bit32u addr, unsigned len, Bit8u *buf);</B>
|
|
<P>Set (write to) physical memory of simulator at address <I>addr</I> from the
|
|
<I>len</I> bytes in <I>buf</I>. The bytes in <I>buf</I> should be copied to the simulator's
|
|
physical memory byte-at-a-time with no concern for endian-ness. Return 1
|
|
if the write is OK, 0 if an error occurs.
|
|
|
|
<P><B>bx_bool (*getphymem)(Bit32u addr, unsigned len, Bit8u *buf);</B>
|
|
<P>Get (read from) physical memory of simulator at address <I>addr</I> to the
|
|
<I>len</I> bytes in <I>buf</I>. The bytes in <I>buf</I> should be copied from the simulator's
|
|
physical memory byte-at-a-time with no concern for endian-ness. Return 1
|
|
if the read is OK, 0 if an error occurs.
|
|
|
|
<P><B>void (*xlate_linear2phy)(Bit32u linear, Bit32u *phy, bx_bool *valid);</B>
|
|
<P>Translate a linear to a physical address, without generating an exception
|
|
or updating the paging tables. The debugger passes the simulator the
|
|
<I>linear</I> address. The simulator is expected to set <I>phy</I> to
|
|
the corresponding physical address if available, and update <I>valid</I>,
|
|
which should be set to 1 if the physical address was available, and 0
|
|
if not. It is possible, the address is not in the paging tables, and
|
|
thus not available.
|
|
|
|
<P><B>bx_bool (*set_reg)(unsigned reg, Bit32u val);</B>
|
|
<P>Set a specific CPU register as determined by <I>reg</I> in the
|
|
simulator to <I>val</I>. The value of <I>reg</I> will be one of the
|
|
defines, such as BX_DBG_REG_EAX defined in <I>debug/debug.h</I>. The
|
|
segment registers can only be set by this method in real mode. This
|
|
function should return a 1 if the operation is successful, and 0 if not.
|
|
|
|
<P><B>Bit32u (*get_reg)(unsigned reg);</B>
|
|
<P>Return the value of a specific CPU register in the simulator as
|
|
determined by <I>reg</I>, whose value is of the same range
|
|
as those passed into <I>set_reg()</I>
|
|
|
|
<P><B>bx_bool (*set_cpu)(bx_dbg_cpu_t *cpu);</B>
|
|
<P>Set all the registers in the CPU simulator to those in the structure
|
|
<I>cpu</I>. The <I>bx_dbg_cpu_t</I> structure is defined in debug/debug.h.
|
|
|
|
<P><B>bx_bool (*get_cpu)(bx_dbg_cpu_t *cpu);</B>
|
|
<P>Get values for all the registers in the CPU simulator, and place them in
|
|
the structure <I>cpu</I>. The <I>bx_dbg_cpu_t</I> structure is defined in debug/debug.h.
|
|
|
|
<P><B>unsigned dirty_page_tbl_size;</B>
|
|
<BR><B>unsigned char *dirty_page_tbl;</B>
|
|
<P>To keep track of what areas of memory have been written to, and allow
|
|
the debugger to efficiently compare regions of memory in each simulator,
|
|
each simulator should provide a dirty page table. It is expected that
|
|
each byte in the array represents one 4K page of physical memory. A value
|
|
of 1 represents that the page has been written to, since the last time it
|
|
was cleared by the debugger, and a value of 0 represents that no write
|
|
has occurred within that physical page.
|
|
<P>You should fill in the field <I>dirty_page_tbl</I> with a pointer
|
|
to an array of bytes. And <I>dirty_page_tbl_size</I> should be set
|
|
to the size of the array in bytes. If possible, make the array big
|
|
enough to span the largest amount of physical memory you might request.
|
|
I chose to not make this size dynamic with the user's requested memory size,
|
|
to eliminate an extra access via a pointer.
|
|
|
|
<P><B>void (*atexit)(void);</B>
|
|
<P>Called when the debugger needs to terminate execution. This function
|
|
should close files and do all necessary shutdown of the simulator.
|
|
<B>NOTE:</B> Do not call exit() directly from
|
|
the simulator, but instead call <I>bx_dbg_exit()</I>. As there may be multiple
|
|
simulators, <I>bx_dbg_exit()</I> will handle invoking the <I>atexit</I> callback in
|
|
each simulator first, and then it will call exit().
|
|
|
|
<P><B>void (*execute)(void);</B>
|
|
<P>The debugger calls <I>execute()</I> in either slave or master simulator,
|
|
commanding either to execute instructions until a guard is reached, in which
|
|
case control should be returned back to the debugger.
|
|
|
|
<P><B>void (*reset_cpu)(unsigned source);</B>
|
|
<P>This function is called once by the debugger to initialize the simulator's
|
|
CPU upon program startup. <I>Source</I> will be either
|
|
BX_RESET_HARDWARE or BX_RESET_SOFTWARE.
|
|
|
|
<P><B>void (*init_mem)(int size_in_bytes);</B>
|
|
<P>This function is called once by the debugger to initialize the simulator's
|
|
memory upon program startup. <I>Size_in_bytes</I> denotes
|
|
the size of physical memory the user has requested, so that both simulators
|
|
can use the same physical memory size. This would be an appropriate
|
|
time to allocate memory for the simulator.
|
|
|
|
<P><B>void (*load_ROM)(const char *path, Bit32u romaddress);</B>
|
|
<P>Commands the simulator to load a ROM image stored in the filename
|
|
<I>path</I> into physical memory at address <I>romaddress</I>.
|
|
|
|
<P><B>void (*set_A20)(unsigned val);</B>
|
|
<P>This function is called by the cosim controller to command either master
|
|
or slave simulator to change it's A20 setting. If the value of <I>val</I> passed
|
|
is 1, then the A20 address line is passed through and used. This will give
|
|
normal addressing of the entire 32bit address space. If the value is 0,
|
|
then A20 is masked out, and addressing at the 1Meg boundary will wrap, modeling
|
|
8086 addressing.
|
|
<P>The cosim controller also commands the <I>bx_pc_system</I> class to
|
|
maintain the same A20 state as passed in this function, so you may alternatively
|
|
use values directly from that class, rather than keep your own A20 state.
|
|
If so, set the value of <I>set_A20</I> to NULL, and use the following
|
|
members from <I>bx_pc_system</I>. In this case, <I>set_A20</I> won't be
|
|
called.
|
|
<UL>
|
|
<LI><B>bx_pc_system.enable_a20:</B> Same value as passed to <I>set_A20</I>.
|
|
<LI><B>bx_pc_system.a20_mask:</B> Logical AND this with a physical address to obtain the
|
|
address after application of the A20 gate.
|
|
</UL>
|
|
|
|
<P><B>void (*set_NMI)(unsigned val);</B>
|
|
<P>Not supported yet. Will tell the simulator that the value of
|
|
the NMI pin, is currently <I>val</I>. Use an empty stub function
|
|
for this for now.
|
|
|
|
<P><B>void (*set_RESET)(unsigned val);</B>
|
|
<P>Not supported yet. Will tell the simulator that the value of
|
|
the RESET pin, is currently <I>val</I>. Use an empty stub function
|
|
for this for now.
|
|
|
|
<P><B>void (*set_INTR)(unsigned val);</B>
|
|
<P>The INTR pin is driven by the device models. When the INTR pin is
|
|
raised due to an interrupt request by the PIC, or lowered after the
|
|
interrupt is acknowledged, this function is called to notify the
|
|
simulator of the new status. Only the master simulator will receive
|
|
notification, as INTR is always 0 for the slave simulator. Interrupts
|
|
are forced in the slave simulator, synchronizing it to the execution
|
|
path of the master, using <I>force_interrupt()</I> described below.
|
|
A value in <I>val</I> of 0 indicates no interrupt is requested. A value
|
|
of 1 indicates an interrupt request.
|
|
|
|
<P><B>void (*force_interrupt)(unsigned vector);</B>
|
|
<P>In order for the debugger to force the slave simulator to take an
|
|
interrupt at the same point as the master simulator, the interrupt vector
|
|
is recorded when taken by the master simulator. The debugger commands
|
|
the slave to the same point, and calls this routine, forcing the slave
|
|
to take the given interrupt, <I>vector</I>.
|
|
|
|
<P><B>void (*instr_start)(void);</B> (Only defined if macro BX_INSTRUMENTATION is 1)
|
|
<P>Called when the user types in "<I>instrument start</I>" at the debug prompt.
|
|
The instrumentation package can use this function to do whatever is
|
|
necessary to initialize the instrumentation package and/or command
|
|
it to begin collecting data.
|
|
|
|
<P><B>void (*instr_stop)(void);</B> (Only defined if macro BX_INSTRUMENTATION is 1)
|
|
<P>Called when the user types in "<I>instrument stop</I>" at the debug prompt.
|
|
The instrumentation package can use this function to do whatever is
|
|
necessary to temporarily or permanently stop the instrumentation package
|
|
from collecting data.
|
|
|
|
<P><B>void (*instr_reset)(void);</B> (Only defined if macro BX_INSTRUMENTATION is 1)
|
|
<P>Called when the user types in "<I>instrument reset</I>" at the debug prompt.
|
|
The instrumentation package can use this function to command the instrumentation
|
|
package to reset it's data collection mechanisms.
|
|
|
|
<P><B>void (*instr_print)(void);</B> (Only defined if macro BX_INSTRUMENTATION is 1)
|
|
<P>Called when the user types in "<I>instrument print</I>" at the debug prompt.
|
|
The instrumentation package can use this function to output it's collected
|
|
data.
|
|
|
|
<P><B>void (*loader)(char *path);</B> (Only defined if macro BX_USE_LOADER is 1)
|
|
<P>Called when the user types in "<I>loader pathname</I>" at the debug prompt.
|
|
The idea is to allow a loader routine to read in a program which is specific
|
|
to the OS you are running within the emulator, from a file on your native
|
|
workstation, load it properly into simulator memory and run it on the
|
|
simulator. This loader must be specific to the OS you are running within the
|
|
simulator, and I do not provide one with bochs.
|
|
|
|
<P><B>void (*take_irq)(void);</B>
|
|
<BR><B>void (*take_dma)(void);</B>
|
|
<BR><B>unsigned (*query_pending)(void);</B>
|
|
<P>These are vestiges of a past interface. They correspond to the
|
|
"take irq", "take dma", and "query pending" commands, which you
|
|
shouldn't use. They will be removed. Set these fields to NULL,
|
|
or to empty stub functions.
|
|
|
|
<HR>
|
|
<HR>
|
|
<H3>Debugger (Cosimulation Controller) Functions:</H3>
|
|
|
|
<P><B>void bx_dbg_exit(int code)</B>
|
|
<P>When there is a situation in the simulator, where you need to terminate
|
|
due to an unrecoverable error (panic), call <I>bx_dbg_exit()</I>. Among
|
|
other things, this function will call the <I>at_exit</I> callback function
|
|
in each simulator, and ultimately call the system exit() function.
|
|
|
|
<P><B>Bit8u bx_dbg_IAC(void)</B>
|
|
<P>The simulator's CPU code should call this function when it is acknowledging an
|
|
interrupt from the PIC via the INTR line. The interrupt vector number from the PIC
|
|
is returned.
|
|
|
|
<P><B>Bit32u bx_dbg_inp(Bit16u addr, unsigned len)</B>
|
|
<P>To read data from an IO device, the simulator should call this function.
|
|
Pass in the IO address <I>addr</I>, and the size of the IO operation <I>len</I>.
|
|
|
|
<P><B>void bx_dbg_outp(Bit16u addr, Bit32u value, unsigned len)</B>
|
|
<P>To write data to an IO device, the simulator should call this function.
|
|
Pass in the IO address <I>addr</I>, and the size of the IO operation <I>len</I>.
|
|
|
|
<P><B>Bit8u bx_dbg_ucmem_read(Bit32u addr)</B>
|
|
<BR><B>void bx_dbg_ucmem_write(Bit32u addr, Bit8u value)</B>
|
|
<P>For memory read/write accesses which fall in the range of 0xA0000 to 0xBFFFF,
|
|
the accesses should not be to directed to the simulator's memory, since
|
|
these are UnCacheable MEMory addresses. The VGA adapter maps it's memory to this
|
|
range. Instead, call these functions to perform reads/writes to memory
|
|
accesses in this range. For <I>bx_dbg_ucmem_read()</I>, pass the physical address
|
|
<I>addr</I>, and the value of the read is returned. For <I>bx_dbg_ucmem_write()</I>,
|
|
pass the physical address <I>addr</I> and value <I>value</I> of the write.
|
|
|
|
<P><B>void bx_dbg_async_pin_ack(unsigned what, bx_bool val)</B>
|
|
<P>In order for the master and slave simulators to accept changes in pins
|
|
such as the A20 line, at the same point, the debugger provides a mechanism
|
|
for pending the pin change, until it is acknowledged by the master simulator.
|
|
The place where the change is ack'd, is recorded by the debugger. This
|
|
information is used to run the slave simulator, forcing it to accept the
|
|
changes at the same locale as did the master.
|
|
|
|
<P>Initially, the IO devices call a function <I>bx_dbg_async_pin_request()</I>,
|
|
not listed here, to record the pin change as pending. The pending status
|
|
is recorded along with the guard information in <I>bx_guard.async_changes_pending.which</I>.
|
|
This field contains a binary OR'd set of pending pin changes. Currently
|
|
only A20 is supported, which is represented by the macro BX_DBG_ASYNC_PENDING_A20.
|
|
|
|
<P>At a time prudent to your CPU simulator, check to see if there are
|
|
any pending changes, that the CPU should acknowledge. If so, acknowledge
|
|
them by calling <I>bx_dbg_async_pin_ack()</I>. The pending value of
|
|
the A20 enable is stored in <I>bx_guard.async_changes_pending.a20</I>.
|
|
Here is some sample code which performs this task, that you can insert
|
|
into the appropriate place in your CPU simulator.
|
|
|
|
<PRE>
|
|
if (bx_guard.async_changes_pending.which) {
|
|
if (bx_guard.async_changes_pending.which & BX_DBG_ASYNC_PENDING_A20)
|
|
bx_dbg_async_pin_ack(BX_DBG_ASYNC_PENDING_A20,
|
|
bx_guard.async_changes_pending.a20);
|
|
// ...other checks here when they are supported
|
|
}
|
|
</PRE>
|
|
|
|
The <I>bx_dbg_async_pin_ack()</I> function will in turn, invoke
|
|
the <I>set_A20()</I> callback function in the master simulator, so you
|
|
don't have to deal with updating local A20 state in your simulator here,
|
|
as long as you handle it in <I>set_A20()</I>. Keep in mind, the slave
|
|
simulator will never see the code inside this sample code if-construct,
|
|
since changes are forced in the slave by the debugger at points where the master
|
|
simulator acknowledged them, not as a direct effect of the IO devices.
|
|
|
|
<HR>
|
|
<HR>
|
|
<H3>Guards:</H3>
|
|
|
|
Guards are a mechanism by which the debugger requests each simulator
|
|
to stop execution and return control back to the debugger. The debugger
|
|
runs each simulator for a particular number of instructions, or until
|
|
certain events occur. Guards are set by the debugger, and it is up
|
|
to each simulator to examine them upon and during execution of the <I>execute()</I>
|
|
callback, and return control back to the debugger when the guard criteria
|
|
are met.
|
|
|
|
<P>Guard information set by the debugger is stored in global structure
|
|
<I>bx_guard</I> of type <I>bx_guard_t</I>. For reference, it's declaration
|
|
is shown here, followed by an explanation of the purpose of each field.
|
|
Information about the guard encountered by the simulator, and which
|
|
caused control to return to the debugger is stored in the global structure
|
|
<I>bx_guard_found[]</I> of type <I>bx_guard_found_t</I>. This is actually
|
|
an array of structures, where <I>bx_guard_found[0]</I> is the first simulator
|
|
with ID 0, and <I>bx_guard_found[1]</I> is the second simulator with
|
|
ID 1. This structure is also declared below, and the text explains
|
|
the information which should be returned in this structure based on
|
|
the guard encountered.
|
|
|
|
<PRE>
|
|
typedef struct {
|
|
unsigned long guard_for;
|
|
|
|
// instruction address breakpoints
|
|
struct {
|
|
#if BX_DBG_SUPPORT_VIR_BPOINT
|
|
unsigned num_virtual;
|
|
struct {
|
|
Bit32u cs; // only use 16 bits
|
|
Bit32u eip;
|
|
unsigned bpoint_id;
|
|
} vir[BX_DBG_MAX_VIR_BPOINTS];
|
|
#endif
|
|
|
|
#if BX_DBG_SUPPORT_LIN_BPOINT
|
|
unsigned num_linear;
|
|
struct {
|
|
Bit32u addr;
|
|
unsigned bpoint_id;
|
|
} lin[BX_DBG_MAX_LIN_BPOINTS];
|
|
#endif
|
|
|
|
#if BX_DBG_SUPPORT_PHY_BPOINT
|
|
unsigned num_physical;
|
|
struct {
|
|
Bit32u addr;
|
|
unsigned bpoint_id;
|
|
} phy[BX_DBG_MAX_PHY_BPOINTS];
|
|
#endif
|
|
} iaddr;
|
|
|
|
bx_dbg_icount_t icount; // stop after completing this many instructions
|
|
|
|
// user typed Ctrl-C, requesting simulator stop at next convient spot
|
|
volatile bx_bool interrupt_requested;
|
|
|
|
// booleans to control whether simulator should report events
|
|
// to debug controller
|
|
struct {
|
|
bx_bool irq;
|
|
bx_bool a20;
|
|
bx_bool io;
|
|
bx_bool ucmem;
|
|
bx_bool dma;
|
|
} report;
|
|
|
|
struct {
|
|
bx_bool irq; // should process IRQs asynchronously
|
|
bx_bool dma; // should process DMAs asynchronously
|
|
} async;
|
|
|
|
#define BX_DBG_ASYNC_PENDING_A20 0x01
|
|
#define BX_DBG_ASYNC_PENDING_RESET 0x02
|
|
#define BX_DBG_ASYNC_PENDING_NMI 0x04
|
|
|
|
// Asynchronous changes which are pending. These are Q'd by
|
|
// the debugger, as the master simulator is notified of a pending
|
|
// async change. At the simulator's next point, where it checks for
|
|
// such events, it notifies the debugger with acknowlegement. This
|
|
// field contains a logically or'd list of all events which should
|
|
// be checked, and ack'd.
|
|
struct {
|
|
unsigned which; // logical OR of above constants
|
|
bx_bool a20;
|
|
bx_bool reset;
|
|
bx_bool nmi;
|
|
} async_changes_pending;
|
|
} bx_guard_t;
|
|
|
|
typedef struct {
|
|
unsigned long guard_found;
|
|
unsigned iaddr_index;
|
|
bx_dbg_icount_t icount; // number of completed instructions
|
|
Bit32u cs; // cs:eip and linear addr of instruction at guard point
|
|
Bit32u eip;
|
|
Bit32u laddr;
|
|
bx_bool is_32bit_code; // CS seg size at guard point
|
|
bx_bool ctrl_c; // simulator stopped due to Ctrl-C request
|
|
} bx_guard_found_t;
|
|
|
|
extern bx_guard_t bx_guard;
|
|
extern bx_guard_found_t bx_guard_found[];
|
|
</PRE>
|
|
|
|
<HR>
|
|
<H3>bx_guard_found[]:</H3>
|
|
It is the task of each simulator to update the <I>bx_guard_found</I>
|
|
structure.
|
|
There are some fields which are specific to the type of guard in
|
|
question, and you should update those when a particular guard is
|
|
encountered. Those fields are explained in more detail in the section
|
|
relating to the specific guard. There are some fields which are
|
|
updated for every case, no matter what the guard is. Below is a list
|
|
and explanation of the usage of each field.
|
|
|
|
<P><B>unsigned long guard_found;</B> this should be filled in with the
|
|
particular guard encountered, for example if an instruction count
|
|
guard is hit, set this to BX_DBG_GUARD_ICOUNT.
|
|
|
|
<P><B>unsigned iaddr_index;</B>
|
|
This field is updated, whenever a virtual/linear/physical instruction
|
|
address guard is hit. It is the array index into the bx_guard.iaddr.vir[],
|
|
bx_guard.iaddr.lin[], or bx_guard.iaddr.phy[] arrays, whichever is appropriate.
|
|
|
|
<P><B>bx_dbg_icount_t icount;</B>
|
|
This contains the number of instructions which have been completely
|
|
executed, when the guard was encountered.
|
|
|
|
<P><B>Bit32u cs;</B>
|
|
<BR><B>Bit32u eip;</B>
|
|
<BR><B>Bit32u laddr;</B>
|
|
<BR><B>bx_bool is_32bit_code;</B>
|
|
These all relate to the same instruction address. From the debugger's
|
|
point of view, instruction addresses can be only at the beginning of
|
|
the instruction. Once an instruction is completed, use the address
|
|
of the next instruction.
|
|
Set <I>cs</I> and <I>eip</I> to the instruction's address (CS:EIP).
|
|
Set <I>laddr</I> to the instruction's corresponding linear address.
|
|
Set <I>is_32bit_code</I> to the size (0=16bit, 1=32bit) of the code
|
|
segment when the guard is encountered. This is used for disassembly.
|
|
|
|
<P><B>bx_bool ctrl_c;</B>
|
|
To allow the user to interrupt a simulator from the debug prompt, the
|
|
debugger traps Ctrl-C interrupts, and sets <I>bx_guard.interrupt_requested</I>.
|
|
Your simulator can optionally look for this, provided that the
|
|
BX_DBG_GUARD_CTRL_C bit is set in <I>bx_guard.guard_for</I> structure.
|
|
If you chose to do so, you may look for this occurrance whenever is
|
|
convenient. Set <I>ctrl_c</I> to 1 to signify this guard has occurred.
|
|
Here' some sample code to demonstrate this:
|
|
<PRE>
|
|
// convenient point to see if user typed Ctrl-C
|
|
if (bx_guard.interrupt_requested && (bx_guard.guard_for & BX_DBG_GUARD_CTRL_C)) {
|
|
bx_guard_found[BX_SIM_ID].guard_found = BX_DBG_GUARD_CTRL_C;
|
|
return; // some mechanism to return control here
|
|
}
|
|
</PRE>
|
|
|
|
<HR>
|
|
<H3>bx_guard:</H3>
|
|
<P><B>unsigned long guard_for;</B>
|
|
<P>This is a binary OR'd list of guards the debugger is requesting each
|
|
simulator to stop on. Only if the corresponding bit is set in this field,
|
|
should the simulator examine the rest of the criteria for that guard.
|
|
Currently, each simulator must be capable of recognizing the following
|
|
guards, and returning to the debugger when they occur:
|
|
<UL>
|
|
<LI>BX_DBG_GUARD_ICOUNT: Instruction count.
|
|
<LI>BX_DBG_GUARD_CTRL_C: User requested interrupt via Ctrl-C
|
|
<LI>BX_DBG_GUARD_IADDR_VIR: Stop on this virtual instruction address
|
|
<LI>BX_DBG_GUARD_IADDR_LIN: Stop on this linear instruction address
|
|
<LI>BX_DBG_GUARD_IADDR_PHY: Stop on this physical instruction address
|
|
</UL>
|
|
|
|
<P><B>struct { .. } iaddr;</B>
|
|
<P>This structure holds the guard information for instruction address
|
|
guards (breakpoints). Depending upon your selections, after editing
|
|
<I>config.h</I> in the main directory (generated by running ./configure),
|
|
certain types of instruction address guards are supported. Which ones,
|
|
are determined by the BX_DBG_SUPPORT_VIR_BPOINT, BX_DBG_SUPPORT_LIN_BPOINT,
|
|
and BX_DBG_SUPPORT_PHY_BPOINT macros.
|
|
|
|
<P>If the <I>guard_for</I> field contains a set bit represented by
|
|
BX_DBG_GUARD_IADDR_VIR, then the <I>iaddr.num_virtual</I> field holds
|
|
the number of virtual instruction address guards to examine and compare
|
|
to the current address. For each, you must examine the CS:EIP values
|
|
stored in <I>iaddr.vir[n]</I>, in the <I>cs</I> and <I>eip</I> subfields.
|
|
If there is a match, record this in the guard found structure, and
|
|
return control to the debugger:
|
|
<PRE>
|
|
bx_guard_found[ID].guard_found = BX_DBG_GUARD_IADDR_VIR;
|
|
bx_guard_found[ID].iaddr_index = n; // array index in bx_guard.iaddr.vir[]
|
|
bx_guard_found[ID].icount = .. // number of completed instructions
|
|
bx_guard_found[ID].cs = .. // CS selector value
|
|
bx_guard_found[ID].eip = .. // EIP value
|
|
bx_guard_found[ID].laddr = .. // linear address of CS:EIP
|
|
bx_guard_found[ID].is_32bit_code = .. // 0=16bit code, 1=32bit code
|
|
// return control here
|
|
</PRE>
|
|
|
|
<P>If the <I>guard_for</I> field contains a set bit represented by
|
|
BX_DBG_GUARD_IADDR_LIN, then the <I>iaddr.num_linear</I> field holds
|
|
the number of linear instruction address guards to examine and compare
|
|
to the current address. For each, you must examine the linear address values
|
|
stored in <I>iaddr.lin[n]</I>, in the <I>addr</I> subfield.
|
|
If there is a match, record this in the guard found structure, and
|
|
return control to the debugger:
|
|
<PRE>
|
|
bx_guard_found[ID].guard_found = BX_DBG_GUARD_IADDR_LIN;
|
|
bx_guard_found[ID].iaddr_index = n; // array index in bx_guard.iaddr.lin[]
|
|
bx_guard_found[ID].icount = .. // number of completed instructions
|
|
bx_guard_found[ID].cs = .. // CS selector value
|
|
bx_guard_found[ID].eip = .. // EIP value
|
|
bx_guard_found[ID].laddr = .. // linear address of CS:EIP
|
|
bx_guard_found[ID].is_32bit_code = .. // 0=16bit code, 1=32bit code
|
|
// return control here
|
|
</PRE>
|
|
|
|
<P>If the <I>guard_for</I> field contains a set bit represented by
|
|
BX_DBG_GUARD_IADDR_PHY, then the <I>iaddr.num_physical</I> field holds
|
|
the number of physical instruction address guards to examine and compare
|
|
to the current address. For each, you must examine the physical address values
|
|
stored in <I>iaddr.phy[n]</I>, in the <I>addr</I> subfield.
|
|
If there is a match, record this in the guard found structure, and
|
|
return control to the debugger:
|
|
<PRE>
|
|
bx_guard_found[ID].guard_found = BX_DBG_GUARD_IADDR_PHY;
|
|
bx_guard_found[ID].iaddr_index = n; // array index in bx_guard.iaddr.phy[]
|
|
bx_guard_found[ID].icount = .. // number of completed instructions
|
|
bx_guard_found[ID].cs = .. // CS selector value
|
|
bx_guard_found[ID].eip = .. // EIP value
|
|
bx_guard_found[ID].laddr = .. // linear address of CS:EIP
|
|
bx_guard_found[ID].is_32bit_code = .. // 0=16bit code, 1=32bit code
|
|
// return control here
|
|
</PRE>
|
|
|
|
|
|
<P><B>volatile bx_bool interrupt_requested;</B>
|
|
<P>If the debugger has turned on the guard for a user interrupt, and
|
|
the user has indeed requested one (Ctrl-C), the debugger will set
|
|
this field to 1. The simulator should record this in the guard found
|
|
information, and return control back to the debugger. Look above at the
|
|
explanation for the <I>bx_guard.interrupt_requested</I> field for some sample code
|
|
on how to do this.
|
|
|
|
<P><B>struct { .. } async;</B>
|
|
<P><B>struct { .. } async_changes_pending;</B>
|
|
|
|
|
|
<HR SIZE=5 NOSHADE>
|
|
<P>
|
|
Related Links:
|
|
<UL>
|
|
<LI><A HREF="debugger.html">Debugger</A>
|
|
<LI><A HREF="instrumentation.html">Instrumentation</A>
|
|
</UL>
|
|
|
|
</BODY>
|
|
</HTML>
|