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:
5
simulators/bochs/docs-html/00README
Normal file
5
simulators/bochs/docs-html/00README
Normal 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
|
||||
118
simulators/bochs/docs-html/biossums.txt
Normal file
118
simulators/bochs/docs-html/biossums.txt
Normal 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.
|
||||
606
simulators/bochs/docs-html/cosimulation.html
Normal file
606
simulators/bochs/docs-html/cosimulation.html
Normal 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>
|
||||
163
simulators/bochs/docs-html/enh_dbg_user_man.txt
Executable file
163
simulators/bochs/docs-html/enh_dbg_user_man.txt
Executable 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.
|
||||
114
simulators/bochs/docs-html/memory.txt
Executable file
114
simulators/bochs/docs-html/memory.txt
Executable 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");
|
||||
12
simulators/bochs/docs-html/random.txt
Normal file
12
simulators/bochs/docs-html/random.txt
Normal 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.
|
||||
|
||||
Reference in New Issue
Block a user