Fail* directories reorganized, Code-cleanup (-> coding-style), Typos+comments fixed.

git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1321 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
adrian
2012-06-08 20:09:43 +00:00
parent d474a5b952
commit 2575604b41
866 changed files with 1848 additions and 1879 deletions

View File

@ -0,0 +1,5 @@
In the past this folder contained the Bochs documentation. After converting
everything to the docbook format, it can be used as a temporary storage for
new stuff before adding it to the official docs.
-Volker Ruppert

View File

@ -0,0 +1,118 @@
biossums
Intention
Writing a bios for a pc-compatible includes the task of embedding various
checksums. At least there is the overall bios checksum stored in the very
last byte of the program. Depending on the number and types of services the
bios provides there are others, e.g.
-- a checksum for the pci bios extensions,
-- a checksum for the plug and play bios extensions,
-- checksums for multiprocessor bios extensions.
All these checksums have one common point: using the usual assembler directives
they are hard (if not impossible) to compute at compile time. You can either
compute them by hand --- a tedious, error-prone, task, where in addition you
often have to make unreliable assumptions about the memory layout of the
entire bios. Or you patch them directly into your compiled bios-image. Apart
from computing the checksums this is what biossums does for you.
Checksums
With the exception of the overall bios checksum, in a modern pc-bios checksums
are not used to ensure data integrity. Instead they are used in conjunction
with certain signatures to securely identify the entry points or the addresses
of important data of some bios-extensions. Because these services are often
invoked from x86 protected mode the original method via interrupts is not
applicable. Scanning (even only parts) of the bios for (short) signatures and
solely relying on this is insecure though, cause the found signature might not
refer to the sought service but rather be some obscure machine code resembling
the signature by accident.
Since signatures are usually part of a larger header or table the above
mentioned problem is being circumvented by checksumming over this header and
comparing the result to a checksum stored next to the signature. In practice the
checksum is often part of the header, chosen in a way that the contents of the
header add up to zero.
Usage
biossums is very simple and straightforward. The only (and mandatory) argument
is the file-name of the bios-image. The file is being read, patched and written.
So if you want to keep your original file for reference, use biossums on a copy
of your bios-image.
For now, biossums can only rely on signatures to find the locations of the
accompanying checksums. Therefore biossums refuses to set any checksums if it
finds more than one signature of the same type.
Example output
Run upon a Bochs bios configured for 2 CPUs (ver. 1.29) biossums displays:
PCI-Bios header at: 0x9480
Current checksum: 0xA9
Calculated checksum: 0xA9
MP header at: 0xD0F0
Current checksum: 0xC1
Calculated checksum: 0xC1
PCMP header at: 0xD000
Current checksum: 0x65
Calculated checksum: 0x65
Bios checksum at: 0xFFFF
Current checksum: 0x00
Calculated checksum: 0x24 Setting checksum.
If we patch in a second "_32_" signature at offset 0x9760 and reset the PCMP
checksum to 0x00 we get:
PCI-Bios header at: 0x9480
Current checksum: 0xA9
Calculated checksum: 0xA9
PCI-Bios header at: 0x9760
Current checksum: 0x00
Calculated checksum: 0x00 Multiple PCI headers! No checksum set.
MP header at: 0xD0F0
Current checksum: 0xC1
Calculated checksum: 0xC1
PCMP header at: 0xD000
Current checksum: 0x00
Calculated checksum: 0x65 Setting checksum.
Bios checksum at: 0xFFFF
Current checksum: 0x00
Calculated checksum: 0x01 Setting checksum.
Possible enhancements
Although biossums takes care of all checksums being used by the bios of the
Bochs project (as of version 2.02) there are more to cover, e.g. the checksums
for "Plug and Play" bios extension.
In addition it was planned to provide further information to biossums via map-/
symbol-files to verify the locations of checksums apart from scanning for
signatures. For now this seems not to be necessary; in practice no double
signatures have been observed yet.

View File

@ -0,0 +1,606 @@
<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="../doc/docbook/images/undercon.png" ALT="Under Construction">
I'll add a discussion of cosimulation here soon.
<HR SIZE=5 NOSHADE>
<H2>Cosimulation Programmatic Interface</H2>
<IMG SRC="../doc/docbook/images/undercon.png" 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 bx_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>bx_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 bx_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 bx_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>

View File

@ -0,0 +1,163 @@
User tips: (ver 1.2)
The main user features available from the menus should be fairly obvious
to anyone who has used bochs -- but here are a few quick explanations, anyway.
These explanations include a few keyboard and mouse shortcuts that you might
not find through experimentation.
Terminology:
The Bochs guys call this GUI debugger interface the CI, to distinguish it
for themselves from the "VGA window" that shows the display of the simulated
computer. I will call this debugger GUI interface the "frontend". It's not
much better of a term, but oh well.
The text debugger interface that you are all familiar with is called the
Bochs Internal Debugger ("ID" for short).
The frontend is organized around 3 main "list-view" windows:
The Register window:
Typically, all the various registers are grouped by color. If you don't like the
colors, they can be turned off, or modified at compile time. There are options
to show or hide most register "groups", so that you can focus more strictly on
the registers you are interested in (probably just the GP registers).
Notes:
Yes, the XMM display shows hex in the "decimal" column -- there is more
room there. Deal with it.
** Doubleclicking a register attempts to change its value. Bochs may not
allow you to change most registers. In future versions, more registers may
be modifiable.
The Disassembly window:
Disassembly output that is autoloaded, or generated from the menu, ends up
here. If the frontend cannot detect the "current instruction" in the list,
when it reaches the next instruction -- then it will autoload a new list.
Having a big list will reduce the number of autoloads, and allows you to see
more. The list can contain up to 2048 lines. However, if you load more than
1000 lines, you are more likely to see performance problems.
Note: There are two kinds of emulated memory in bochs: Linear and Physical.
Emulated Linear memory is mapped onto Physical memory by x86 virtual memory
methods (paging and segmentation). If paging and segmenataion are "off", or
"identity mapped", then both "types" of memory mean the same thing. But they
still work a little differently. With the Internal Debugger, you can set
breakpoints to either kind of memory, separately. Normally, you would use
the "b" command to set breakpoints in physical mem, and "lb" to set breakpoints
in linear mem. This frontend ONLY displays linear breakpoints. It does not
bother trying to figure out the linear->phsical reverse mapping to show
physical breakpoints. (There are also "virtual" breakpoints that are also
not shown.) All the types of breakpoints still WORK, it is just that you
will not see them marked on the screen.
It will be obvious to you that the current instruction is marked in green,
unless it is on a breakpoint, when it turns blue. Breakpoints are red, of
course.
** You must click a line in the window, before you can use frontend commands
to set or clear a linear breakpoint on it.
** You can doubleclick (which saves steps) to set or clear a linear breakpoint.
The MemDump window:
As of this version, the MemDump window isn't much more than a display of the
contents of memory. In later versions, hopefully it will be expanded into a
fairly fully-featured hexeditor. You can dump either phyical mem, or linear
mem. There are breakpoint-like things (that work with physical memory only,
currently), called "watchpoints". A physical memory address can cause a break
in the simulation if it is read, or written.
The frontend again does NOT try to calculate out the linear -> physical mapping
in any attempt to display the physical watchpoints while viewing linear mem.
You must click a hex byte (on a physical mem dump that shows bytes), in order to
set or clear a read and/or write watchpoint on that byte. Read watchpoints are
green (on black), write watchpoints are red, watchpoints that are both write
and read are blue. There is a hardcoded limit in bochs of 16 of each type of
watchpoint.
The MemDump window loads/shows 4K of memory at a time.
** PageUp/Down scrolls the display up or down through mem, 2K at a time.
** Doubleclicking a line of memory allows you to change the byte values.
(Works on both linear and physical mem dumps.)
** Doubleclicking with the Shift key down sets write watchpoints.
** Doubleclicking with Alt sets read watchpoints.
** You need to click once on the memory window before you can use its "Find"
function. The Find function is pretty limited in scope, currently. It can
only find bytes (or strings of bytes) within each 16byte "line".
Other windows:
The Output window shows anything that the Bochs Internal Debugger tries to send
to you. The window is scrollable, but only keeps a limited history of output (10K).
The ID is always spamming you with "Next at t=" and disassembly lines, that would
tend to fill up the Output window with garbage -- so there are options to ignore
either of these types of output.
The Input window is for sending user commands directly into the Bochs Internal
Debugger -- bypassing the frontend. Results will appear in the Output Window.
The Input window has a history feature for commands, using the Up and Down arrows --
it remembers 64 commands, 80 bytes each. No matter where you click on the frontend,
you can always type directly into the Input box without clicking on it.
When the Input window is invisible, you should still be able to type into it --
after taking into account the bug listed at the bottom of this file.
** Hitting Enter on a blank line will cause a Singlestep.
The Param Tree:
The bochs param_tree shows the internal state of most of bochs. It will be
expanded in the future to show even more. You can see the detailed state of
all cpu registers -- including the "hidden" parts (look in the "bochs" branch).
Or see the current state of most of the emulated hardware.
The Stack window:
The MemDump windows do not automatically refresh -- except for the Stack
window. If you leave the stack window active, it will update as the stack
changes. If you want to update the other MemDump windows with fresh data,
hit Refresh.
The Breakpoint/Watchpoint window:
Doubleclicking will delete a breakpoint or watchpoint.
The Command Button row:
Just a (hopefully) convenient way of using the mouse, instead of the keyboard.
If you don't like them, or they take up too much space, you can turn them off.
The CPU Button row:
This only shows up when you are running a multi-cpu simulation. Click on the
CPU that you want to view. All CPUs are always stepped together, and they all
stop the first time one hits some sort of breakpoint.
Docking/Resizing
If you grab one of the two vertical "bars" between the lists, you can horizontally
resize the lists. The cursor will change, but there will be no animation.
If you grab the middle of one of the lists, and drag it on top of one of the
other lists, you can reorder the positions of the lists on the screen. The
cursor will change, but there will be no animation. You can set an alternate
"docking order" at compile time, also, if you have a permanent preference.
(See the top of the wenhdbg_h.h file, for compile-time customization.)
Additional Notes:
If you have a really big GDT or Paging display in the MemDump window, and you
select a different display, it may take several seconds to delete the big display
before it can switch.
Uppercase text tends to seem a little annoying, but it really is a lot easier to
read, especially on a proportional font. If you change to a fixed font, then you
may want to switch the display to lowercase.
KDE Users: The gtk-qt-engine drawing functions of Ver 0.71 (or anything earlier
than 1.0?) draw all text as black, instead of in the correct foreground colors.
Fix: Upgrade to 1.1.

View File

@ -0,0 +1,114 @@
Memory access handling explanation
by Brendan Trotter:
For the local APIC/s, any read or write to a CPU's own local APIC is
handled internally and does not go to the bus. If the read/write misses
this area then the read/write does go to the bus (where other CPU's ignore
it).
This means if 2 CPUs have different local APIC addresses and one CPU tries
to write to the area used by the second CPU's local APIC, then it will go
to the bus and will not access the second CPU's local APIC.
This applies in all cases (e.g. hyper-threading and dual core work the same).
For I/O APICs, the device is on the bus and should override anything that
is "underneath" it. For example, if you relocate the I/O APIC to
0x00000000, then a read or write to this area will not reach the RAM
underneath. In a similar way, if someone maps a PCI device to 0xFEC00000
(or somewhere that overlaps the I/O APIC) then a write to this area will
not reach the PCI device.
This leads to something like the following for accesses originating from a
CPU:
if (address_is_within_this_CPUs_local_APIC_area)
do_local_APIC_access();
else if (address_is_within_an_I/O_APIC_area)
do_I/O_APIC_access();
else if (address_is_within_a_PCI_device_area)
do_PCI_access();
else if (address_is_within_RAM_area)
do_RAM_access();
else printf("Bogus address!\n");
For an accesses originating from a PCI device (e.g. PCI bus masters), there
is no access to any CPUs local APIC. It'd go like:
if (address_is_within_an_I/O_APIC_area)
do_I/O_APIC_access();
else if (address_is_within_a_PCI_device_area)
do_PCI_access();
else if (address_is_within_RAM_area)
do_RAM_access();
else printf("Bogus address from PCI device!\n");
In both cases it is complicated by the configuration of the PCI host
controller/s and any "PCI to PCI" bridges. Fortunately this can be ignored
by Bochs as it doesn't support PCI bridges (except for the host controller
itself which can handle all accesses). Bochs may need to worry about the
"PCI to LPC" bridge though. For example, even though a PCI device can
read/write to the I/O APIC, an ISA device behind the PCI to LPC bridge
can't. This means for an ISA bus master you'd have something like:
if (address_is_within_a_PCI_device_area)
do_PCI_access();
else if (address_is_within_RAM_area)
do_RAM_access();
else printf("Bogus address from PCI device!\n");
This complicates things for the ISA DMA controllers, which should not be
able to read/write to the I/O APIC - for e.g. if the I/O APIC base is set
to 0x00000000, then an ISA DMA transfer that writes to 0x00000000 should
write to RAM not the I/O APIC (a PCI bus master would write to the I/O APIC
in the same situation).
I'm not convinced modelling real hardware 100% correctly is necessary
though - it would only matter for very rare situations (e.g. when the OS
stuffs things up badly). A normal OS will not stuff things up like this
(i.e. a normal OS won't map the I/O APIC to an area that overlaps RAM or
anything else). For OS developers (who might stuff things up), it'd
probably be better to panic anyway - e.g. "BX_PANIC: I/O APIC base set to
an address that overlaps RAM or a memory mapped device".
In general, the CPU has an "address bus" which consists of data lines,
address lines and 2 others lines. One of these other lines is the "I/O
select" line - if you do "mov [0x000000AA],al" and then do "out 0xAA,al"
you'd get almost the same thing on the CPUs bus (the only difference would
be the state of the "I/O select" line). When the CPU does an access that is
intended for I/O port space it just asserts the "I/O select" line.
The second line is for SMM which works just like the I/O select line.
When the CPU accesses a memory location normally the "SMM select" line is
not asserted and normal memory is accessed. When the CPU is in SMM mode the
"SMM select" line is asserted for memory accesses. This means that the CPU
can use 3 completely seperate address spaces (one for normal memory, one
for I/O space and another for SMRAM). How the chipset treats these lines
depends on what the CPU is used for - for example, these lines could be
ignored so that all types of accesses are the same (which means I/O port
instructions would access memory locations from 0x00000000 to 0x0000FFFF
and there'd be no seperate SMRAM area). For "PC compatible" computers the
"I/O select" line does select a completely seperate address space, but the
"SMM select" line does not. Instead, the chipset uses it to disable access
to the video display memory (and enable access to the RAM underneath).
Fortunately, access to the SMRAM area is also controlled by the chipset,
such that the CPU can access SMRAM regardless of whether it asserts it's
"SMM select" line or not. As mentioned in my previous email, for the I440FX
chipset it's called the System Management RAM Control Register (or SMRCR),
and is in the PCI host controller's PCI configuration space at offset 0x72.
Returning to what I wrote earlier, this leads to something like the
following for accesses originating from a CPU:
if (address_is_within_this_CPUs_local_APIC_area)
do_local_APIC_access();
else if ((CPU_is_in_SMM_mode || chipset_SMRCR_enabled) && address_is_within_SMM_area)
do_SMM_access();
else if (address_is_within_an_I/O_APIC_area)
do_I/O_APIC_access();
else if (address_is_within_a_PCI_device_area)
do_PCI_access();
else if (address_is_within_RAM_area)
do_RAM_access();
else printf("Bogus address!\n");

View File

@ -0,0 +1,12 @@
look at http://www.ao.net/help/software/lps/disktools/INDEX
asbench.zip Benchmark for drives under a ASPI device driver
ataid011.zip ATA Identification. Echos Identify Drive data
atrt4mb.exe AT Regression test
and most interesting of all:
ext2tool.zip Mount and use Ext2 filesystems from DOS
From: Gregg Eshelman <g_alan_e@yahoo.com>
Sounds good. :) Some Windows disk imaging apps
are WinImage and SH (Schenk and Horne) Copy Star.