2001-04-10 05:04:59 +04:00
|
|
|
<HTML>
|
|
|
|
|
|
|
|
<HEAD>
|
2001-04-10 06:20:02 +04:00
|
|
|
<META NAME="copyright" CONTENT="Copyright 2001 by MandrakeSoft S.A.">
|
2001-04-10 05:04:59 +04:00
|
|
|
<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>
|
|
|
|
|
2005-01-08 23:50:18 +03:00
|
|
|
<IMG SRC="../doc/docbook/images/undercon.png" ALT="Under Construction">
|
2001-04-10 05:04:59 +04:00
|
|
|
I'll add a discussion of cosimulation here soon.
|
|
|
|
|
|
|
|
<HR SIZE=5 NOSHADE>
|
|
|
|
<H2>Cosimulation Programmatic Interface</H2>
|
2005-01-08 23:50:18 +03:00
|
|
|
<IMG SRC="../doc/docbook/images/undercon.png" ALT="Under Construction">
|
2001-04-10 05:04:59 +04:00
|
|
|
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
|
2003-11-28 18:07:29 +03:00
|
|
|
<I>bx_dbg_callback_t</I> is defined in bx_debug/debug.h. Most fields
|
2001-04-10 05:04:59 +04:00
|
|
|
are function pointers, though some are not. Below, is a description
|
|
|
|
of each field in the callback structure.
|
|
|
|
|
|
|
|
<P>
|
|
|
|
<PRE>
|
|
|
|
typedef struct {
|
2002-10-25 15:44:41 +04:00
|
|
|
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);
|
2001-04-10 05:04:59 +04:00
|
|
|
Bit32u (*get_reg)(unsigned reg);
|
2002-10-25 15:44:41 +04:00
|
|
|
bx_bool (*set_cpu)(bx_dbg_cpu_t *cpu);
|
|
|
|
bx_bool (*get_cpu)(bx_dbg_cpu_t *cpu);
|
2001-04-10 05:04:59 +04:00
|
|
|
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>
|
|
|
|
|
2002-10-25 15:44:41 +04:00
|
|
|
<P><B>bx_bool (*setphymem)(Bit32u addr, unsigned len, Bit8u *buf);</B>
|
2001-04-10 05:04:59 +04:00
|
|
|
<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.
|
|
|
|
|
2002-10-25 15:44:41 +04:00
|
|
|
<P><B>bx_bool (*getphymem)(Bit32u addr, unsigned len, Bit8u *buf);</B>
|
2001-04-10 05:04:59 +04:00
|
|
|
<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.
|
|
|
|
|
2002-10-25 15:44:41 +04:00
|
|
|
<P><B>void (*xlate_linear2phy)(Bit32u linear, Bit32u *phy, bx_bool *valid);</B>
|
2001-04-10 05:04:59 +04:00
|
|
|
<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.
|
|
|
|
|
2002-10-25 15:44:41 +04:00
|
|
|
<P><B>bx_bool (*set_reg)(unsigned reg, Bit32u val);</B>
|
2001-04-10 05:04:59 +04:00
|
|
|
<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
|
2003-11-28 18:07:29 +03:00
|
|
|
defines, such as BX_DBG_REG_EAX defined in <I>bx_debug/debug.h</I>. The
|
2001-04-10 05:04:59 +04:00
|
|
|
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>
|
|
|
|
|
2002-10-25 15:44:41 +04:00
|
|
|
<P><B>bx_bool (*set_cpu)(bx_dbg_cpu_t *cpu);</B>
|
2001-04-10 05:04:59 +04:00
|
|
|
<P>Set all the registers in the CPU simulator to those in the structure
|
2003-11-28 18:07:29 +03:00
|
|
|
<I>cpu</I>. The <I>bx_dbg_cpu_t</I> structure is defined in bx_debug/debug.h.
|
2001-04-10 05:04:59 +04:00
|
|
|
|
2002-10-25 15:44:41 +04:00
|
|
|
<P><B>bx_bool (*get_cpu)(bx_dbg_cpu_t *cpu);</B>
|
2001-04-10 05:04:59 +04:00
|
|
|
<P>Get values for all the registers in the CPU simulator, and place them in
|
2003-11-28 18:07:29 +03:00
|
|
|
the structure <I>cpu</I>. The <I>bx_dbg_cpu_t</I> structure is defined in bx_debug/debug.h.
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
<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.
|
|
|
|
|
2002-10-25 15:44:41 +04:00
|
|
|
<P><B>void bx_dbg_async_pin_ack(unsigned what, bx_bool val)</B>
|
2001-04-10 05:04:59 +04:00
|
|
|
<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
|
2002-10-25 15:44:41 +04:00
|
|
|
volatile bx_bool interrupt_requested;
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// booleans to control whether simulator should report events
|
|
|
|
// to debug controller
|
|
|
|
struct {
|
2002-10-25 15:44:41 +04:00
|
|
|
bx_bool irq;
|
|
|
|
bx_bool a20;
|
|
|
|
bx_bool io;
|
|
|
|
bx_bool ucmem;
|
|
|
|
bx_bool dma;
|
2001-04-10 05:04:59 +04:00
|
|
|
} report;
|
|
|
|
|
|
|
|
struct {
|
2002-10-25 15:44:41 +04:00
|
|
|
bx_bool irq; // should process IRQs asynchronously
|
|
|
|
bx_bool dma; // should process DMAs asynchronously
|
2001-04-10 05:04:59 +04:00
|
|
|
} 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
|
2002-10-25 15:44:41 +04:00
|
|
|
bx_bool a20;
|
|
|
|
bx_bool reset;
|
|
|
|
bx_bool nmi;
|
2001-04-10 05:04:59 +04:00
|
|
|
} 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;
|
2002-10-25 15:44:41 +04:00
|
|
|
bx_bool is_32bit_code; // CS seg size at guard point
|
|
|
|
bx_bool ctrl_c; // simulator stopped due to Ctrl-C request
|
2001-04-10 05:04:59 +04:00
|
|
|
} 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>
|
2002-10-25 15:44:41 +04:00
|
|
|
<BR><B>bx_bool is_32bit_code;</B>
|
2001-04-10 05:04:59 +04:00
|
|
|
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.
|
|
|
|
|
2002-10-25 15:44:41 +04:00
|
|
|
<P><B>bx_bool ctrl_c;</B>
|
2001-04-10 05:04:59 +04:00
|
|
|
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>
|
|
|
|
|
|
|
|
|
2002-10-25 15:44:41 +04:00
|
|
|
<P><B>volatile bx_bool interrupt_requested;</B>
|
2001-04-10 05:04:59 +04:00
|
|
|
<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>
|