Bochs/bochs/docs-html/bxdebugger.html

335 lines
14 KiB
HTML
Raw Normal View History

<HTML>
<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>
<HR>
<HR>
<H3>Debugger 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.
2021-02-04 22:21:47 +03:00
<P><B>void bx_dbg_async_pin_ack(unsigned what, 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
2021-02-04 22:21:47 +03:00
volatile bool interrupt_requested;
// booleans to control whether simulator should report events
// to debug controller
struct {
2021-02-04 22:21:47 +03:00
bool irq;
bool a20;
bool io;
bool ucmem;
bool dma;
} report;
struct {
2021-02-04 22:21:47 +03:00
bool irq; // should process IRQs asynchronously
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
2021-02-04 22:21:47 +03:00
bool a20;
bool reset;
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;
2021-02-04 22:21:47 +03:00
bool is_32bit_code; // CS seg size at guard point
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>
2021-02-04 22:21:47 +03:00
<BR><B>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.
2021-02-04 22:21:47 +03:00
<P><B>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>
2021-02-04 22:21:47 +03:00
<P><B>volatile 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>
</BODY>
</HTML>